zoukankan      html  css  js  c++  java
  • 有关最小生成树的一些心得

    求最小生成树的两种算法:

    1.Kruskal算法(本质是贪心)

    1. 把图中的所有边按代价从小到大排序;

    2.按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。

    这种做法可以通过并查集来维护连通性,总体复杂度是O(m+并查集的玄学复杂度);

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<string>
    using namespace std;
    int n,m,i,j,u,v,total;
    struct edge{
        int start,to;long long val;
    }bian[2000005];
    int f[100000];
    long long ans;
    
    int find(int x)//并查集部分
    {
        if (f[x]==x) return x; else 
        {
            f[x]=find(f[x]);
            return f[x];
        }   
    }
    
    bool cmp(edge a,edge b)//结构体快排时用到的
    {
        return a.val<b.val;
    }
    
    inline void kruskal()//最小生成树
    {
    
        for(int i=1;i<=m;i++)
        {
            u=find(bian[i].start);
            v=find(bian[i].to);
            if(u==v) continue;//判断在不在同一个并查集里面,在就下一个循环
                ans+=bian[i].val;//不在,就加上
                f[u]=v;//连接两个并查集
                total++;
                if(total==n-1) break;//当形成了最小生成树后,退出(之后做的也没用了)
        }
    } 
    int main()
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++) f[i]=i;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&bian[i].start,&bian[i].to,&bian[i].val);
        }
        sort(bian+1,bian+m+1,cmp);//快排边长
        kruskal();
        printf("%d",ans);
        return 0;
    }

    2.Prim算法(本质还是贪心)

    每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。

    时间复杂度是O(n^n);

    但注意,每次寻找最短的一条边的时候,我们可以用堆来快速维护,使复杂度降至O((n+m)logm);

    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define R register int
    using namespace std;
    
    int k,n,m,cnt,sum,ai,bi,ci,head[5005],dis[5005],vis[5005];
    
    struct Edge
    {
        int v,w,next;
    }e[400005];
    
    void add(int u,int v,int w)
    {
        e[++k].v=v;
        e[k].w=w;
        e[k].next=head[u];
        head[u]=k;
    }
    
    typedef pair <int,int> pii;
    priority_queue <pii,vector<pii>,greater<pii> > q;
    
    void prim()
    {
        dis[1]=0;
        q.push(make_pair(0,1));
        while(!q.empty()&&cnt<n)
        {
            int d=q.top().first,u=q.top().second;
            q.pop();
            if(vis[u]) continue;
            cnt++;
            sum+=d;
            vis[u]=1;
            for(R i=head[u];i!=-1;i=e[i].next)
                if(e[i].w<dis[e[i].v])
                    dis[e[i].v]=e[i].w,q.push(make_pair(dis[e[i].v],e[i].v));
        }
    }
    
    int main()
    {
        memset(dis,127,sizeof(dis));
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for(R i=1;i<=m;i++)
        {
            scanf("%d%d%d",&ai,&bi,&ci);
            add(ai,bi,ci);
            add(bi,ai,ci);
        }
        prim();
        if (cnt==n)printf("%d",sum);
        else printf("orz");
    }

    例题:洛谷P1265 公路修建

    #include <bits/stdc++.h>
    using namespace std;
    double x[5005],y[5005];
    double dis[5005],ans;
    bool v[5005];
    double query(double x,double x1,double y,double y1)
    {
        return (x-x1)*(x-x1)+(y-y1)*(y-y1);
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1; i<=n; i++) scanf("%lf%lf",&x[i],&y[i]);
        memset(dis,0x7f,sizeof(dis));
        dis[1]=0;
        for(int i=1;i<=n;i++){
            int k=0;
            for (int j=1; j<=n; j++) if(!v[j]&&dis[j]<dis[k]) k=j;
            v[k]=1;
            for (int j=1; j<=n; j++) if(!v[j]&&query(x[k],x[j],y[k],y[j])<dis[j]) dis[j]=query(x[k],x[j],y[k],y[j]);          
        }
        for(int i=1;i<=n;i++) ans+=sqrt(dis[i]);
        printf ("%.2lf
    ",ans);
        return 0;
    }

    从策略上来说,Prim算法是直接查找,多次寻找邻边的权重最小值,而Kruskal是需要先对权重排序后查找的~

    所以说,Kruskal在算法效率上是比Prim快的,因为Kruskal只需一次对权重的排序就能找到最小生成树,而Prim算法需要多次对邻边排序才能找到~

    prim:该算法的时间复杂度为O(n2)。与图中边数无关,该算法适合于稠密图。

    kruskal:需要对图的边进行访问,所以克鲁斯卡尔算法的时间复杂度只和边又关系,可以证明其时间复杂度为O(eloge)。适合稀疏图

    一句话来说,prim适合对于点进行运算,kruskal适合对于边进行运算;、

  • 相关阅读:
    JAVA语言基础
    JAVA程序 从命令行接受多个数字,求和之后输出结果
    构建之法阅读笔记02
    软件工程学习进度第三周
    软件工程个人作业02
    安装Linux
    软件工程学习进度
    软件工程个人作业01
    构建之法阅读笔记01
    登录界面
  • 原文地址:https://www.cnblogs.com/kamimxr/p/11523020.html
Copyright © 2011-2022 走看看