最小生成树
最小生成树(Minimum Spanning Tree)问题是图论中的一个经典问题,它的目标是在一个连通加权无向图中找到一棵边权值和最小的生成树。解决最小生成树问题的算法常见的有Kruskal算法和Prim算法。
Kruskal算法
假设连通网G=(V,E),令最小生成树的初始状态为只有n个顶点而无边的非连通图,概述图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点分别在T中不同的连通分量上,则将此边加入到T中;否则,舍去此边而选择下一条代价最小的边。依此类推,直至T中所有顶点构成一个连通分量为止。
具体实现
实现Kruskal算法的最主要两个核心:一个是找到最小的边和判断两个节点是否在同一个连通分量上。
每次找最小的边,实际上就是对输入的边进行排序(如果是prim算法则使用优先队列),这样就可以得到边从小到大的集合:
struct w{
int x,y,l;
};//定义边结构体
bool cmp(w &m1,w &m2){
return m1.l<m2.l;
}//自定义边的比较方式
w v[80];//边集
sort(v,v+len,cmp);//排序
判断是否在同一个连通分量上可以使用并查集算法:
int count=0;
int sum=0;
sort(v,v+p,cmp);
for(int i=0;i<p;i++){
if(Find(v[i].x)!=Find(v[i].y)){//如果不在同一连通分量,则将边加入
f[Find(v[i].x)]=Find(v[i].y);//连接两个点的连通分量
sum+=v[i].l;
count++;
}
if(count==n-1) break;//对于n点的图,最小生成树有n-1条边,程序可以提前退出
}
完整代码:
#include<cstdio>
#include<algorithm>
using namespace std;
struct w{
int x,y,l;
};
bool cmp(w &m1,w &m2){
return m1.l<m2.l;
}
w v[80];
int f[30];
void init(int n){
for(int i=0;i<=n;i++){
f[i]=i;
}
}
int Find(int x){
while(x!=f[x]){
x=f[x]=f[f[x]];
}
return x;
}
int main(){
int n;
while(scanf("%d",&n)&&n!=0){
init(n);
char s,t;
int a,b,c;
int p=0;
for(int i=0;i<n-1;i++){//输入边集
cin>>s>>a;
b=s-'A'+1;
for(int j=0;j<a;j++){
cin>>t>>c;
v[p].x=b;
v[p].y=t-'A'+1;
v[p].l=c;
p++;
}
}
int count=0;
int sum=0;
sort(v,v+p,cmp);
for(int i=0;i<p;i++){
if(Find(v[i].x)!=Find(v[i].y)){
f[Find(v[i].x)]=Find(v[i].y);
sum+=v[i].l;
count++;
}
if(count==n-1) break;
}
printf("%d\n",sum);
}
return 0;
}