题目链接 点击打开链接
最小生成树模板题,我用的kruskal,大致思路是对所有路从小到大排序,每次选最小的路,并且选的路不能形成环路。
判断环路用到并查集的思想,添加新的路前,先用并查集find一下路两边的点祖先,如果祖先一样,说明两个点联通,添加该路会形成环路,所以continue下一次循环,否则如果祖先不一样就添加这条路。一直重复下去直到添加n-1条路。
#include <algorithm> #include <cstdio> #include <cstring> #define INF 0xfffffff using namespace std; int n,m; int pre[100]; //并查集的上级数组 int G[100][100]; //图 struct Edge{ //存边的端点 int begin,end; int len; //边长 }edge[20000]; bool cmp(Edge a,Edge b){ return a.len < b.len; } int num = 0; int Find(int x){ //找祖先 while(pre[x] != x) x = pre[x]; return x; } void Union(int x,int y){ //合并2个集合 int xx = Find(x); int yy = Find(y); if(xx == yy) return; pre[xx] = yy; } void kruskal(){ if(num == 0){ printf("0\n"); return; } sort(edge,edge+num,cmp); int sl = 0; //长度和 int se = 0; //边数和 (sum of edge) int i = 0; while(true){ int x = edge[i].begin; int y = edge[i].end; int xx = Find(x); int yy = Find(y); if(xx == yy){ i++; continue; } Union(x,y); se++; sl += edge[i].len; i++; if(se == n-1) break; } printf("%d\n",sl); } int main(){ while(scanf("%d",&n) != EOF){ if(n == 0) break; scanf("%d",&m); for(int i = 1;i <= n;i++) pre[i] = i; for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++){ if(i == j) G[i][j] = 0; else G[i][j] = INF; } num = 0; int a,b,x; for(int i = 0;i < m;i++){ scanf("%d%d%d",&a,&b,&x); G[a][b] = G[b][a] = min(x,G[b][a]); } for(int i = 1;i <= n;i++) for(int j = i+1;j <= n;j++){ if(G[i][j] == INF) continue; edge[num].begin = i; edge[num].end = j; edge[num].len = G[i][j]; num++; } kruskal(); } return 0; }