本文主要介绍了最小生成树的两种解决办法: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 }