Kruskal是有关于最小生成树的算法。
这个算法非常好理解,用一句话来概括就是:
从小到大找不同集合的边。
那么,具体是怎样的呢。
1.先把所有顶点初始化为一个连通分量。
2.从所有边中选择最小的(指权值)边,判断该边是否已经在当前构造的连通分量中,如果是,则放弃这条边,找下一条边。
3.重复第二步,直到所有点都被纳入到当前构造的连通分量中。
所以,这样的算法涉及到了查询当前边是否在集合中和合并一条边到集合中。
于是想到了并查集。
先贴并查集代码,并查集的思想是用一个父亲数组:fa[i]代表节点i的父亲,以此将某节点并为某节点儿子来合并,查询。
#include<iostream> using namespace std; int par[10001],ran[10001]; void init(int N) { for(int k=0;k<N;k++) { par[k]=k; } } int find(int x) { if(par[x]==x) { return x; } else { return find(par[x]); } } void unionn(int x,int y) { x=find(x); y=find(y); if(ran[x]<ran[y]) { par[x]=y; } else { par[y]=x; if(ran[x]==ran[y]) { ran[x]++; } } } bool same(int x,int y) { return find(x)==find(y); } int main() { int n,m,a,b,c; cin>>n>>m; init(n+1); for(int i=0;i<m;i++) { cin>>a>>b>>c; if(a==1) { unionn(b,c); } else { if(same(b,c)) { cout<<"Y"<<endl; } else { cout<<"N"<<endl; } } } return 0; }
然后,我们需要根据这份代码来进行克鲁斯卡算法。
根据之前的思路,可以写出这样的算法:
void kruskal(){ sort(e+1,e+m+1,cmp);
int sum = 0; int num = 0; for(int i = 1;i <= m;i++){ int s = e[i].s; int t = e[i].t; if(!same(s,t)){ printf("连接%d,%d ",s,t); sum += e[i].v; union_(s,t); num++; } if(num >= n-1) break; } printf("最小生成树权值和为%d,共有%d个节点",sum,num); }
看起来似乎很好理解。
这也是一个贪心的思想。