连通无向图有最小生成树,边权从小到大排序,每次尝试加入权最小的边,如果不成圈,就把这边加进去,所有边扫一遍就求出了最小生成树。
判断连通分支用Union-Set(并查集),就是把连通的点看成一个集合,只关心哪些点在一个集合里,而不关心相互的连接方式。x父节点用fa【x】保存;如果x没有父节点,fa【x】 = x。查找一条长链的时候每次用递归把链上的点的父节点全设置成根节点,方便下次查找。思路看上去挺简单的,然而程序调试了好久。开始把边按无向图那样正反各存一次,其实是没必要的,反正每条边考察一次;剩下的就是细节问题,码力不足到处出错。测试了一组数据:
代码如下:
#include<bits/stdc++.h> using namespace std; const int maxm = 20007; struct Edge { int u, v, w; Edge(){} Edge(int u, int v, int w):u(u), v(v), w(w){} }E[maxm];//只存边就好啦,不用把从一个点出发的边穿起来 int n, m; int r[maxm], fa[maxm]; int cmp(int x, int y) { return E[x].w < E[y].w; } int findbaba(int x) { return fa[x] == x? x : fa[x] = findbaba(fa[x]);//找父节点 } int kruskal() { int ans = 0; vector<int> path;//存路径 for(int i = 0; i <= n; i++) fa[i] = i;//每个人的爸爸都是自己(误) for(int i = 0; i < m; i++) r[i] = i;//把边的序号放一个数组里 sort(r, r+m, cmp);//移动序号比移动struct容易吧 for(int i = 0; i < m; i++) { int e = r[i]; int x = findbaba(E[e].u); int y = findbaba(E[e].v); if(x != y)//加入边e后不成圈 { ans += E[e].w; fa[x] = y; path.push_back(e); } } for(int i = 0; i < path.size(); i++) { int e = path[i]; printf("%d<->%d :%d ", E[e].u, E[e].v, E[e].w); }//打印路径 return ans;//最小权和 } int main() { //freopen("in.txt", "r", stdin); int t; scanf("%d", &t); while(t--) { scanf("%d%d", &n, &m); for(int i = 0; i < m; i++) { scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w); } printf(" %d ", kruskal()); } return 0; }