最小生成树-Kruskal算法

来源:互联网 发布:天猫淘宝 编辑:程序博客网 时间:2024/06/17 01:00

Kruskal算法

最小生成树是典型的贪心法求解的问题,假如有N个节点,则最小生成树会有N-1条边,要每条边权值之和最小,只需要每次选出当前权值最小的边,如果当前边会形成环路,则这条边是无效的也就是加入进去会是冗余的,因为这条边新连通的是两个已经被别的边连通了的节点,如果不会形成环路,就加入这条边,这样就能保证最终的权值之和最小。

具体写法:

判环路一般借助并查集来实现。

而每次提取出权值最小的边,我们可以用结构体来存放边,然后事先通过排序将所有边按照权值由小到大进行排序,然后我们就可以在O(1)的时间内找到当前权值最小的边,当然也可以借助优先队列。

先定义存放边的结构体,同时重载了<运算符和定义了一个存放全部边的数组。

struct edge{   int node1;   int node2;   int value;   bool operator < (edge x){      return value<x.value;   }}Array[MaxEdgeNum];

进行排序,直接调用库函数。

sort(Array,Array+N);
利用并查集来判断是否会形成环路,这里假设存放Array数组N个元素的父亲节点或者说标志节点的数组为par[N],Find函数用于寻找边的两个端节点所属集合的根节点或者说标志节点,join函数完成这两个节点所属的两个集合的合并和判断是否会形成环路,如果在合并之前两个节点已经同属于一个集合,这时如果再将连接这两个节点的边放入生成树的集合,则会形成回路,举个例子,某个图只有a,b,c三个节点并且相互连通,我们先进行join(a,b),join(b,c)操作连通a,b和b,c,这时已经有一条路径连通三个节点了,如果这时在进行join(a,c)操作连通a,c的话就会形成环路,这里设定如果会产生环路的话返回true。

void init(){   mem(par);   for(int i=1;i<=N;i++){      par[i]=i;   }}int FindPar(int x){   int a=x,b;   while(a!=par[a])      a=par[a];   while(x!=par[x]){      b=par[x];      par[x]=a;      x=b;   }   return a;}bool join(int a,int b){   int x=FindPar(a);   int y=FindPar(b);   if(x!=y){      par[x]=y;      return true;   }   return false;}

挑选最小生成树的N-1条边,如果找不出足够的N-1条边,说明不存在生成树,图不连通,这里返回 -1代表无解。

long long kruskal(){   int MSTnum=0,ans=0;   init();   sort(Array,Array+EdgeNum);   for(int i=0;i<EdgeNum;i++){      if(join(Array[i].node1,Array[i].node2)){         MSTnum++;         ans+=Array[i].value;         if(MSTnum==N-1)            break;      }   }   if(MSTnum==N-1)      return ans;   else      return -1;}

如果存在有不存在生成树的情况只需要检查一下选出的边的数量,如果最终这个变量小于N-1,则图不连通,无解,并查集中也可以判断一下所有节点在最终是否都并入了一个集合,但这显然不如前一种方法。
完整代码示例:(题目:hdu-1863)

#include<iostream>#include<cstring>#include<algorithm>using namespace std;#define mem(a) memset(a,0,sizeof(a))#define MaxN 1005#define MaxEdgeNum 2000int par[MaxN],N,EdgeNum;void init(){   mem(par);   for(int i=1;i<=N;i++){      par[i]=i;   }}int FindPar(int x){   int a=x,b;   while(a!=par[a])      a=par[a];   while(x!=par[x]){      b=par[x];      par[x]=a;      x=b;   }   return a;}bool join(int a,int b){   int x=FindPar(a);   int y=FindPar(b);   if(x!=y){      par[x]=y;      return true;   }   return false;}struct edge{   int node1;   int node2;   int value;   bool operator < (edge x){      return value<x.value;   }}Array[MaxEdgeNum];long long kruskal(){   int MSTnum=0,ans=0;   init();   sort(Array,Array+EdgeNum);   for(int i=0;i<EdgeNum;i++){      if(join(Array[i].node1,Array[i].node2)){         MSTnum++;         ans+=Array[i].value;         if(MSTnum==N-1)            break;      }   }   if(MSTnum==N-1)      return ans;   else      return -1;}int main(){   while(cin>>EdgeNum>>N&&EdgeNum){      for(int i=0;i<EdgeNum;i++){         cin>>Array[i].node1>>Array[i].node2>>Array[i].value;      }      int ans=kruskal();      if(ans==-1){         cout<<'?'<<endl;      }      else{         cout<<ans<<endl;      }   }}


原创粉丝点击