题目链接 点击打开链接
最小生成树模板题,我用的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;
}