一、kruskal实现步骤
1.将图中的所有边升序排序
2.初始化森林集合,现在森林集合中每一个点都是一棵树。
3.遍历图中的边从小到大地判断边的端点是否在同一棵树上:
如果在:说明这个边放进去会形成环,放弃这个边,继续遍历。
如果不在:把这个边放进答案树中
4.直到森林集合中只剩一棵树,停止遍历,这棵树就是最小生成树。
5.如果遍历结束后森林中还有至少两棵树,说明这个图没有最小生成树。
二、为什么kruskal是对的
简单说就是因为取边是从小到大取的,所以加进答案树的边是目前可用的最小且不会生成环的边,这个边一定是答案的一部分。
复杂地举例子来说:比如这个图如果取大边(2,3)先入,因为形成环而舍弃的边就会变成(1,3),也就是说舍弃了更小的边。为求最优解一定是从小判断,如果这个边能放入答案树(也就是放进去不会形成环)那么这个边一定是局部最优解,多个局部最优解合成的一定也是全局的最优解。
三、代码实现(并查集判断环)
#include<bits/stdc++.h>
using namespace std;
const int N = 100010,M = 2*N;
int p[N];//并查集,i的爹是p[i](不一定是祖宗,但用find函数一定能找到祖宗)
int n,m;//n为点数,m为边数
struct edge
{
int a,b,c;
}edges[M];//存边
int find(int x)
{
if(p[x] != x)p[x] = find(p[x]);//找祖宗节点的同时更新p数组
//“如果(我爹)不是自己,那么找(我爹的爹)的同时把(我爹)更新成(我爹的爹)”
return p[x];
}
bool cmp(const edge &a,const edge &b)
{
return a.c < b.c;
}//自定义sort比较规则
int main()
{
cin >> n >> m;
for(int i = 1;i<=n;i++)p[i] = i;//初始化并查集(
for(int i = 0;i<m;i++)
{
int a,b,c;
cin >> a >> b >> c;
edges[i] = {a,b,c};
}
sort(edges,edges+m,cmp);//按边长升序排序edges
int ans = 0,cnt = 0;//ans存MST权重和,cnt存MST中边数
for(int i = 0;i<m;i++)
{
int a = edges[i].a,b = edges[i].b,c = edges[i].c;
int aa = find(a),bb = find(b);
if(aa != bb)
{
p[bb] = aa;
ans += c;
cnt ++;
}
}
if(cnt!=n-1)cout << "impossible";//加入的边不够n-1条,就没有生成MST
else cout << ans;
return 0;
}
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6