图 求最小生成树 Prim Kruskral算法

图 求最小生成树 Prim Kruskral算法

  1. Prim 算法(和Dijkstra算法极像)

    • 寻找一个到最小生成树集合距离最近的点
    • 把这个点加入到最小生树集合中
    • 再用刚刚加入的点去更新集合到集合外的点的最短距离 重复步骤2 (共迭代n(顶点个数)次)
    //时间复杂度 O(V^2) 适合稠密图 用邻接矩阵
    #include<iostream>
    using namespace std;
    const int INF=0x3f3f3f;
    const int MAX_V = 600;
    int d[MAX_V],G[MAX_V][MAX_V];
    bool used[MAX_V];
    int Prim();
    int n,m;
    
    int main() {
        fill(G[0],G[0]+MAX_V*MAX_V,INF);//邻接矩阵一定要初始化
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++) {
            int a,b,w;
            scanf("%d%d%d",&a,&b,&w);
            G[a][b]=G[b][a]=min(G[a][b],w); //有重边选择小的
        }
        int t=Prim();
        if(t==INF) printf("impossible");
        else printf("%d",t);
    }
    
    int Prim() {
        fill(used,used+MAX_V,false); //初始化
        fill(d,d+MAX_V,INF);
        int res=0;  //记录最小生成树的总权值
        for(int i=0;i<n;i++) {
            int v=-1;
            for(int j=1;j<=n;j++){
                if(!used[j]&&(v==-1||d[v]>d[j])) v=j;
            }
            if(i&&d[v]==INF) return INF; //不是第一次迭代距离集合最近的距离为INF 说明没有最小生成树 图不连通
            if(i)  res+=d[v];  //第一次迭代只是加入一个点 没有加入一条边所以第一次迭代执行
            used[v]=true;
            for(int j=1;j<=n;j++) { //更新集合到集合外的点的最小距离
                d[j] = min(d[j],G[v][j]);
            }
        }
        return res;
    }
    
  2. Kruskal算法

    • 先用sort把所有边按权重从小到大排序
    • 再遍历每一条边,如果一条边的两点不连通(用并查集结构查看每条边的两个顶点是否在一个集合中,不在)就把这条边加入到最小生成树中(同时把两个顶点合并在一个集合中)
    //时间复杂度O(E log E)
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N=2e6;
    int p[N];  //利用并查集 判断边的两点是否在一个集合 不再就加入一个集合(最小生成树)
    struct edge{  //存取所有边
        int a,b,w;
    };
    edge es[N];
    bool cmp(edge a,edge b);
    int find(int x);
    
    bool cmp(edge a,edge b){
        return a.w<b.w;
    }
    
    int find(int x){
        if(p[x]==x) return x;
        return p[x]=find(p[x]);
    }
    int n,m;
    int main() {
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++) {
            int a,b,w;
            scanf("%d%d%d",&a,&b,&w);
            es[i]={a,b,w};
        }
        sort(es,es+m,cmp);   //把边按权值从小到大排序
        for(int i=0;i<=n;i++){  //初始化并查集
            p[i]=i;
        }
        int res=0,cnt=0;  //cnt为边数  res为最小生成树总权重
        for(int i=0;i<m;i++){  //遍历每一条边
            int a=find(es[i].a);
            int b=find(es[i].b);
            if(a!=b){  //边的两点不在一个集合
                p[a]=b;
                res+=es[i].w;
                cnt++;
            }
        }
        if(cnt<n-1) printf("impossible");  //边数不够n-1条说明没有最小生成树
        else printf("%d",res);
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值