码迷,mamicode.com
首页 > 编程语言 > 详细

最小生成树-kruskal算法

时间:2018-03-10 16:05:07      阅读:206      评论:0      收藏:0      [点我收藏+]

标签:false   方法   直接   设定   运算   pac   star   processor   pid   

Kruskal算法

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

具体写法:

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

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

先定义存放边的结构体,同时重载了<运算符。

 

[cpp] view plain copy
 
  1. struct edge{  
  2.    int a,b,value;  
  3.    edge(int x=0,int y=0,int z=0):a(x),b(y),value(z){}  
  4.    bool operator <(edge x){  
  5.       return value<x.value;  
  6.    }  
  7. };  

 

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

 

[cpp] view plain copy
 
  1. sort(all,all+N); //all存放全部边  

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

 

[cpp] view plain copy
 
  1. int par[maxN];  
  2.   
  3. void init(){  
  4.    for(int i=0;i<maxN;i++)par[i]=i;  
  5. }  
  6.   
  7. int find(int x){  
  8.    if(par[x]!=x)par[x]=find(par[x]);  
  9.    return par[x];  
  10. }  
  11.   
  12. bool join(int a,int b){  
  13.    int x=find(a),y=find(b);  
  14.    if(x!=y){  
  15.       par[x]=y;  
  16.       return true;  
  17.    }  
  18.    return false;  
  19. }  

 

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

 

[cpp] view plain copy
 
  1. long long kruskal(){  
  2.    init();  
  3.    int MSTnum=0;long long ans=0;  
  4.    sort(all.begin(),all.end());  
  5.    for(int i=0;i<all.size();i++)if(join(all[i].a,all[i].b)){ //即不会构成环的情况  
  6.       ans+=all[i].value;  
  7.       MSTnum++;  
  8.       if(MSTnum==N-1)return ans; //找到符合情况的N-1条边  
  9.    }  
  10.    return -1; //如果能找到N-1条边这条语句并不会被执行  
  11. }  

 

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

 

[cpp] view plain copy
 
    1. #include<bits/stdc++.h>  
    2. using namespace std;  
    3.   
    4. const int maxN=10050;  
    5. int N;  
    6. struct edge{  
    7.    int a,b,value;  
    8.    edge(int x=0,int y=0,int z=0):a(x),b(y),value(z){}  
    9.    bool operator <(edge x){  
    10.       return value<x.value;  
    11.    }  
    12. };  
    13. vector<edge> all;  
    14.   
    15. int par[maxN];  
    16.   
    17. void init(){  
    18.    for(int i=0;i<maxN;i++)par[i]=i;  
    19. }  
    20.   
    21. int find(int x){  
    22.    if(par[x]!=x)par[x]=find(par[x]);  
    23.    return par[x];  
    24. }  
    25.   
    26. bool join(int a,int b){  
    27.    int x=find(a),y=find(b);  
    28.    if(x!=y){  
    29.       par[x]=y;  
    30.       return true;  
    31.    }  
    32.    return false;  
    33. }  
    34.   
    35. long long kruskal(){  
    36.    init();  
    37.    int MSTnum=0;long long ans=0;  
    38.    sort(all.begin(),all.end());  
    39.    for(int i=0;i<all.size();i++)if(join(all[i].a,all[i].b)){  
    40.       ans+=all[i].value;  
    41.       MSTnum++;  
    42.       if(MSTnum==N-1)return ans;  
    43.    }  
    44.    return -1;  
    45. }  
    46.   
    47. int main(){  
    48.    int m,a,b,c;  
    49.    while(cin>>m>>N&&m){  
    50.       all.clear();  
    51.       for(int i=0;i<m;i++){  
    52.          cin>>a>>b>>c;  
    53.          all.push_back(edge(a,b,c));  
    54.       }  
    55.       int ans=kruskal();  
    56.       if(ans==-1)cout<<‘?‘<<endl;  
    57.       else cout<<ans<<endl;  
    58.    }  
    59. }  

最小生成树-kruskal算法

标签:false   方法   直接   设定   运算   pac   star   processor   pid   

原文地址:https://www.cnblogs.com/TAMING/p/8538923.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!