Algorithm Design——最小生成树

本文主要介绍了最小生成树的两种解决办法:kruskal以及prim 

  1 /**
  2 Kruskal算法流程:
  3     (1)初始时所有节点属于孤立的集合;
  4     (2)按照边权递增顺序遍历所有的边,若遍历到的边两个节点仍分属于不同的
  5     集合(该边即为联通这两个结合的边中权值最小的那条),则确定该边为最小生
  6     成树上的一条边,并将这两个顶点分属的集合合并;
  7     (3)遍历完所有的边之后,原图上所有节点属于同一个集合则被选取的边和原图
  8     中所有节点构成最小生成树;否则原图不连通,最小生成树不存在。
  9 
 10 
 11 样例输入: 
 12 3
 13 1 2 1 
 14 1 3 2 
 15 2 3 4 
 16 4
 17 1 2 1 
 18 1 3 4 
 19 1 4 1 
 20 2 3 3
 21 2 4 2 
 22 3 4 5 
 23 0
 24 样例输出: 
 25 3 
 26 5
 27 */
 28 
 29 #include<cstdio>
 30 #include<algorithm>
 31 using namespace std;
 32 
 33 #define N  101
 34 
 35 int Tree[N];
 36 
 37 int findRoot(int x)//找到代表集合的树的根节点
 38 {
 39     if(Tree[x] == -1)
 40         return x;
 41     else
 42     {
 43         int tmp = findRoot(Tree[x]);
 44         Tree[x] = tmp;//x直接指向根节点
 45         return tmp;
 46     }
 47 }
 48 
 49 struct Edge//边结构体
 50 {
 51     int a,b;//边两个顶点的编号
 52     int cost;//该边的权值
 53 
 54     bool operator < (const Edge &A) const//重载<运算符
 55     {
 56         return cost < A.cost;
 57     }
 58 }edge[6000];
 59 
 60 int main()
 61 {
 62     int n;
 63     while(scanf_s("%d", &n) != EOF && n != 0)
 64     {
 65         for(int i = 1 ; i <= n * (n - 1) / 2 ; i ++)
 66         {
 67             scanf_s("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].cost);//输入边的顶点以及权值
 68         }
 69 
 70         sort(edge + 1, edge + 1 + n * (n - 1) / 2);//按照边权值递增排序所有的边
 71 
 72         for(int i = 1 ; i <= n ; i ++)
 73         {
 74             Tree[i] = -1;//初始时所有的节点都是孤立的集合
 75         }
 76 
 77         int ans = 0;//最小生成树上边权值的和,初始值是0
 78         for(int i = 1 ; i <= n * (n - 1) / 2 ; i ++)//按照权值递增顺序遍历所有的边
 79         {
 80             int a = findRoot(edge[i].a);
 81             int b = findRoot(edge[i].b);//查找该边两个顶点的信息结合
 82 
 83             if(a != b)//若它们属于不同的集合,则选用该边
 84             {
 85                 Tree[a] = b;//合并两个集合
 86                 ans += edge[i].cost;//累加该边权值
 87             }
 88         }
 89 
 90         printf_s("%d
", ans);//输出
 91     }
 92 }
 93 
 94  /**
 95 prim算法流程:
 96 (1)设置两个集合V和S,任意选择一个顶点作为起始顶点,将起始顶点放入集合S,
 97 其余顶点放入集合V;
 98 (2)然后使用贪心策略,选择一条长度最短并且顶点分别在S和V中的边(即为最小
 99 生成树中的一条边),将这条边在V中的顶点加入到集合S中;
100 (3)循环执行(2),直到S中包含了所有顶点。
101 
102 样例输入: 
103 3
104 1 2 1 
105 1 3 2 
106 2 3 4 
107 4
108 1 2 1 
109 1 3 4 
110 1 4 1 
111 2 3 3
112 2 4 2 
113 3 4 5 
114 0
115 样例输出: 
116 3 
117 5
118 */
119 
120 #include<cstdio>
121 #include<cstring>
122 using namespace std;
123 
124 #define MaxInt 0x3f3f3f3f
125 #define N 110
126 int n;
127 
128 int map[N][N];//存放图
129 int low[N];//存放每两个节点之间最小权值
130 int visited[N];//标记某节点是否已经被访问
131 
132 int prim()
133 {
134     int pos, min, result = 0;
135     memset(visited, 0, sizeof(visited));
136 
137     //从某点开始,分别标记和记录该点
138     visited[1] = 1;
139     pos = 1;
140 
141     for(int i = 1; i <= n ; i ++)
142     {
143         if(i != pos)
144             low[i] = map[pos][i];
145     }
146 
147     for(int i = 1; i < n; i ++)
148     {
149         min = MaxInt;
150         //找到最小生成树节点集合K与剩余节点集合之间距离最小的点
151         for(int j = 1; j <= n; j ++)
152         {
153             if(visited[j] == 0 && min > low[j])
154             {
155                 min = low[j];
156                 pos = j;
157             }
158         }
159 
160         result += min;//最小权值累加
161         visited[pos] = 1;//标记该点
162         //更新权值
163         for(int j = 1 ; j <= n ; j ++)
164         {
165             if(visited[j] == 0 && low[j] > map[pos][j])
166                 low[j] = map[pos][j];
167         }
168     }
169 
170     return result;
171 }
172 
173 int main()
174 {
175     int ans;
176 
177     while(scanf_s("%d", &n) != EOF && n != 0)
178     {
179         memset(map, 0, sizeof(map));
180         int a, b, c;
181         for(int i = 1 ; i <= n * (n - 1) / 2 ; i ++)
182         {
183             scanf_s("%d%d%d", &a, &b, &c);//输入边的顶点以及权值
184             map[a][b] = c;
185             map[b][a] = c;
186         }
187 
188         for(int i = 1 ; i <= n ; i ++)
189         {
190             for(int j = 1 ; j <= n ; j ++)
191             {
192                 if(i == j)
193                     map[i][j] = 0;
194                 else if(map[i][j] == 0)
195                     map[i][j] = MaxInt;
196             }
197         }
198 
199         ans = prim();
200         printf_s("%d
", ans);
201     }
202 }
原文地址:https://www.cnblogs.com/yiyi-xuechen/p/3452303.html