Kruskal算法
最小生成树是典型的贪心法求解的问题,假如有N个节点,则最小生成树会有N-1条边,要每条边权值之和最小,只需要每次选出当前权值最小的边,如果当前边会形成环路,则这条边是无效的也就是加入进去会是冗余的,因为这条边新连通的是两个已经被别的边连通了的节点,如果不会形成环路,就加入这条边,这样就能保证最终的权值之和最小。
具体写法:
判环路一般借助并查集来实现。
而每次提取出权值最小的边,我们可以用结构体来存放边,然后事先通过排序将所有边按照权值由小到大进行排序,然后我们就可以在O(1)的时间内找到当前权值最小的边,当然也可以借助优先队列。
先定义存放边的结构体,同时重载了<运算符。
- struct edge{
- int a,b,value;
- edge(int x=0,int y=0,int z=0):a(x),b(y),value(z){}
- bool operator <(edge x){
- return value<x.value;
- }
- };
进行排序,直接调用库函数。
- 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。
- int par[maxN];
- void init(){
- for(int i=0;i<maxN;i++)par[i]=i;
- }
- int find(int x){
- if(par[x]!=x)par[x]=find(par[x]);
- return par[x];
- }
- bool join(int a,int b){
- int x=find(a),y=find(b);
- if(x!=y){
- par[x]=y;
- return true;
- }
- return false;
- }
挑选最小生成树的N-1条边,如果找不出足够的N-1条边,说明不存在生成树,图不连通,这里返回 -1代表无解。
- long long kruskal(){
- init();
- int MSTnum=0;long long ans=0;
- sort(all.begin(),all.end());
- for(int i=0;i<all.size();i++)if(join(all[i].a,all[i].b)){ //即不会构成环的情况
- ans+=all[i].value;
- MSTnum++;
- if(MSTnum==N-1)return ans; //找到符合情况的N-1条边
- }
- return -1; //如果能找到N-1条边这条语句并不会被执行
- }
如果存在有不存在生成树的情况只需要检查一下选出的边的数量,如果最终这个变量小于N-1,则图不连通,无解,并查集中也可以判断一下所有节点在最终是否都并入了一个集合,但这显然不如前一种方法。
完整代码示例:(题目:hdu-1863)
- #include<bits/stdc++.h>
- using namespace std;
- const int maxN=10050;
- int N;
- struct edge{
- int a,b,value;
- edge(int x=0,int y=0,int z=0):a(x),b(y),value(z){}
- bool operator <(edge x){
- return value<x.value;
- }
- };
- vector<edge> all;
- int par[maxN];
- void init(){
- for(int i=0;i<maxN;i++)par[i]=i;
- }
- int find(int x){
- if(par[x]!=x)par[x]=find(par[x]);
- return par[x];
- }
- bool join(int a,int b){
- int x=find(a),y=find(b);
- if(x!=y){
- par[x]=y;
- return true;
- }
- return false;
- }
- long long kruskal(){
- init();
- int MSTnum=0;long long ans=0;
- sort(all.begin(),all.end());
- for(int i=0;i<all.size();i++)if(join(all[i].a,all[i].b)){
- ans+=all[i].value;
- MSTnum++;
- if(MSTnum==N-1)return ans;
- }
- return -1;
- }
- int main(){
- int m,a,b,c;
- while(cin>>m>>N&&m){
- all.clear();
- for(int i=0;i<m;i++){
- cin>>a>>b>>c;
- all.push_back(edge(a,b,c));
- }
- int ans=kruskal();
- if(ans==-1)cout<<‘?‘<<endl;
- else cout<<ans<<endl;
- }
- }