转载请注明出处:http://blog.youkuaiyun.com/wangjian8006
一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图联通的最少的边。而最小生成树是使这些找到的最小的边权值之和达到最小。
最小生成树有两个算法:prim和kruskal
prim的思想是不段的找点
kruskal的思想是不断的找边
下面来介绍这两个算法
prim简单的描述下就是,在已知点集V中,找到所有V里面的点的邻边,首次保证相邻的点不在V里,取权值最小的边,再将相邻点加进V,这样不断的找,最后找到的n-1条边与n个顶点就是最小生成树。
其实prim和dijkstra的思想差不多,都是一个贪心的策略,只不过在更新条件上改成,取邻边里的最小边就可以了
void prim(){
int i,j,v;
int d[MAXV],vis[MAXV];
for(i=1;i<=n;i++){
d[i]=map[1][i];
vis[i]=0;
}
for(i=1;i<=n;i++){
int min=inf;
for(j=1;j<=n;j++)
if(!vis[j] && d[j]<min){ //找最小点加进来
min=d[j];
v=j;
}
vis[v]=1;
for(j=1;j<=n;j++){
if(!vis[j] && map[v][j]<d[j]) //取邻边的最小边
d[j]=map[v][j];
}
}
for(d[0]=0,i=1;i<=n;i++) d[0]+=d[i];
printf("%d\n",d[0]);
}
kruskal
他是一个不断找边的过程,并且是一个贪心的思路,既然是贪心,那么肯定是从权值小的边开始往已知边集里面添加,首先要将边从小到大开始排序,然后从小到大试着将边往里面加,如果形成环我们就不加进来,如果不形成环就加进来,这样加到n-1条边为止,在判断是否有环,我们要用到并查集,因为kruskal的对象是边,所以适合稀疏图
#include <iostream>
using namespace std;
#define MAXM 15010
#define MAXV 1010
typedef struct{
int s,t,w;
}Edge;
int n,m,set[MAXV];
Edge edge[MAXM];
int find(int x){
int rt;
if(set[x]!=x){
rt=find(set[x]);
set[x]=rt;
return set[x];
}else return x;
}
bool Union(int a,int b){
int fa,fb;
fa=find(a);
fb=find(b);
if(fa==fb) return 0; //如果形成环就退出
set[fa]=fb;
return 1;
}
void kruskal(){
int i,sum=0,cnt=0;
for(i=1;i<=m;i++){
if(Union(edge[i].s,edge[i].t)){
sum+=edge[i].w;
if(++cnt==(n-1)) break; //找到n-1条边
}
}
printf("%d\n",sum);
}
int cmp(const void * a,const void *b){
return (*(Edge*)a).w-(*(Edge*)b).w;
}
int main(){
int i;
while(~scanf("%d%d",&n,&m)){
for(i=0;i<=n;i++) set[i]=i;
for(i=1;i<=m;i++){
scanf("%d%d%d",&edge[i].s,&edge[i].t,&edge[i].w);
}
qsort(edge+1,m,sizeof(edge[0]),cmp); //将边从小到大排序
kruskal();
}
return 0;
}