kruskal算法学习笔记【最小生成树】

恩,,今天感觉有点亏,好好的代码,硬是看了两个小时,结果是一个函数返回值类型错了,蠢哭了给我。。


kruskal算法思想:

       本质上也是一种贪心算法,是比较好理解的。【涉及到一些并查集的思想,可以提前了解下】

       给定一个图A,将该图所含所有边按长度排序【当然不止是数值上的排序,其两端带着的两点也需要紧紧跟随着这条命运未定的边】,既然是求最小生成树,当然所有边的和越小越好,同样是一条边两个点,当然是最短的那条边最优秀,那么就把最短的那条边以及其两端的点挑选出来;

       此时再去找第二短的边,看看这个小同学的两边连着的点是否跟已有的点重复【指两个点都重复,如果只有一个点重复,则正好与已有图连接】,若重复,则舍去该边;此时再去找第三短的边,同样检查与现有图是否重复,如此循环,直至边的数目达到n-1条,【为啥是n-1条?n个点,最短用n-1条边相连~】;

       如果达不到n-1条边,则该图没有最小生成树。【emmm,,难道不是只要是连通图就一定会有最小生成树吗?只有不连通的图才可怜兮兮的没有最小生成树吧】

       简单来说,就是:将边按权值排序,每次从剩下的边集中选择最小的且两个端点不在同一集合的边加入生成树中,反复操作,直到加入了n-1条边。

算法步骤:

       【默默感觉我的思想已经把步骤说的挺清楚了,希望不会太啰嗦】

       1》将图G中的边按权值从小到大快排;【自定义排序规则】

       2》按照权值从小到大依次选边,若当前选取的边加入后使生成树形成了环,则舍弃当前边;否则标记当前边并计数

       3》重复操作2,直至生成树中包含n-1条边。否则当遍历完所有的边后,不到n-1条边,则 表示最小生成树不存在。

实现代码:

       【代码!!】

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100005;
struct Edge{int x,y,z;}a[maxn];
int n,m,ppp[maxn],ans=0,bj;
bool cmp(const Edge &x,const Edge &y){return x.z<y.z;}//自定义排序

int jiaobaba(int x){        //!!here!!why me write "bool"??????!!!!!!返回值一直是1,我差点砸电脑,,真的是。。
    if(ppp[x]==x)return x;
    ppp[x]=jiaobaba(ppp[x]);
    return ppp[x];
}

void kruskal(){
    int p1,p2,k,i;
    k=0;
    for(int i=1;i<=n;i++)ppp[i]=i;//初始化
    for(int i=1;i<=m;i++){
        p1=jiaobaba(a[i].x);
        p2=jiaobaba(a[i].y);
        if(p1!=p2){         //!!!重要点,我的bug直接影响到了这里,所以常含泪水。
            ans+=a[i].z;
            ppp[p1]=p2;     //合并不相同的两个集合
            k++;
            if(k==n-1) break;
        }
    }
    if(k<n-1){             //边数不够,该图没有最小生成树
        cout<<"Impossible"<<endl;
        bj=0;
        return ;
    }
}

int main(){
    cin>>n>>m;
    ans=0;
    bj=1;
    for(int i=1;i<=m;i++){
        cin>>a[i].x>>a[i].y>>a[i].z;
    }
    sort(a+1,a+m+1,cmp);
    kruskal();
    if(bj)cout<<ans<<endl;
    return 0;
}

/*
5 10
1 2 1
1 4 3
1 5 4
1 3 2
2 4 8
2 5 6
2 3 5
4 3 7
4 5 9
5 3 10
*/
/*
4 6
1 2 1
1 3 2
1 4 3
2 3 1
2 4 2
3 4 1
*/

/*
见证我泪奔的错误代码
#include    <iostream>
#include    <cstdio>
#include    <cstring>
#include    <cmath>
#include    <algorithm>
using namespace std;
const int maxn=100005;

struct Edge{
    int x,
        y,
        z;
}a[maxn];

int n,
    m,
    prt[maxn],
    ans=0,
    bj;
bool cmp(const Edge &x,const Edge &y){
    return x.z<y.z;
}

bool Getfather(int x){        //!!!bug!!!
    cout<<"  x="<<x<<endl;
    cout<<"  prt["<<x<<"]="<<prt[x]<<endl;
    if(prt[x]==x) return x;
    prt[x]=Getfather(prt[x]);
    cout<<"      prt["<<x<<"]=Getfather(prt["<<x<<"])="<<prt[x]<<endl;
    return prt[x];
}

void kruskal(){     //核心程序
    int f1,f2,k,i;
    k=0;
    for(int i=1;i<=n;i++)       //初始化
        prt[i]=i;
    for(int i=1;i<=m;i++){

cout<<"\n!!!!a[1].y="<<a[1].y<<endl;
cout<<"i="<<i<<endl<<"  prt[]=";
for(int ii=1;ii<=m;ii++){
cout<<prt[ii]<<" ";
}cout<<endl;

cout<<"\n!!!!a[1].y="<<a[1].y<<endl;
        f1=Getfather(a[i].x);
cout<<"\n!!!!a[1].y="<<a[1].y<<endl;
        f2=Getfather(a[i].y);

cout<<"  f1=Getfather(a["<<i<<"].x)="<<"Getfather("<<a[i].x<<")="<<f1<<endl;
cout<<"  f2=Getfather(a["<<i<<"].y)="<<"Getfather("<<a[i].x<<")="<<f2<<endl;

cout<<"\n!!!!a[1].y="<<a[1].y<<endl;
        if(f1!=f2)
        {
            ans+=a[i].z;
cout<<"ans="<<ans<<endl;
            prt[f1]=f2;     //合并两个不相同的集合
cout<<"prt["<<f1<<"]="<<prt[f1]<<"="<<f2<<endl;
            k++;
            if(k==n-1)break;
        }
    }
    if(k<n-1){
        cout<<"Impossible"<<endl;
        bj=0;
        return ;
    }
}

int main()
{
    cin>>n>>m;
    ans=0;bj=1;
    for(int i=1;i<=m;i++){
        cin>>a[i].x>>a[i].y>>a[i].z;
    }
    for(int i=1;i<=m;i++){
        cout<<"a["<<i<<"].x="<<a[i].x<<"   a["<<i<<"].y="<<a[i].y<<"  a["<<i<<"].z="<<a[i].z<<endl;
    }
cout<<"\n!!!!a[1].y="<<a[1].y<<endl;
    sort(a+1,a+m+1,cmp);
    kruskal();
    if(bj)
        cout<<ans<<endl;
    return 0;
}
*/

以前看不懂代码的时候总想找大神的,能把代码讲的容易理解的那种,等我自己看懂了,,真的不想扯这个,的确有的实现挺变态的,好的变态或者拐弯拐的变态,所以是“模板”一样的存在。

猜你喜欢

转载自blog.csdn.net/sodacoco/article/details/83758683