zoukankan      html  css  js  c++  java
  • 最小生成树

    Kruskal算法

    算法描述:

    把所有边从小到大排序,能加就加,用并查集维护联通性,如果一个边两边的点已经在同一个连通块里了就不要这条边了

    时间复杂度O(mlogm)

    code:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define ll long long
    #define maxn 200010
    using namespace std;
    
    ll n,m,sum=0,cnt=0;
    ll fa[maxn];
    
    struct bcj{
        void init(ll n){
            for(int i=1;i<=n;i++) fa[i]=i;
        }
        ll f(ll x){
            return fa[x]=(fa[x]==x)?x:f(fa[x]);
        }
        bool u(ll x,ll y){
            x=f(x),y=f(y);
            if(x==y) return false;
            fa[x]=y;
            return true;
        }
    }s;
    
    struct edge{
        ll u,v,w;
        bool friend operator < (edge a,edge b){
            return a.w<b.w;
        }
    }e[maxn];
    
    int main(){
        cin>>n>>m;
        s.init(n);
        for(int i=1;i<=m;i++) cin>>e[i].u>>e[i].v>>e[i].w;
        sort(e+1,e+m+1);
        for(int i=1;i<=m;i++)
            if(s.u(e[i].u,e[i].v)){
                sum+=e[i].w;
                cnt++;
            }
        if(cnt!=n-1){
            cout<<"orz"<<endl;
            return 0;
        }
        cout<<sum<<endl;
        return 0;
    } 

    Prim算法

    算法描述:

    随便找一个点当树根,接下来每次加入一个新点,选择离当前生成树最近的点加入就可以了

    时间复杂度O(n2),可用二叉堆优化到O(mlogn),主要用于稠密图尤其是完全图的最小生成树的求解

    code1:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #define ll long long
    #define maxn 200010
    using namespace std;
    template<typename T>
    inline void read(T &x){
        x=0; bool flag=0; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
        for(;isdigit(c);c=getchar()) x=x*10+(c^48);
        if(flag) x=-x;
    }
    
    struct data{
        ll dis;ll id;
        bool friend operator < (data a,data b){
            return a.dis>b.dis;
        } 
    };
    priority_queue <data> pq;
    ll v[maxn<<1],x[maxn<<1],w[maxn<<1],al[maxn],ct;//maxn<<1相当于maxn*2 
    bool visit[maxn];
    void add(ll U,ll V,ll va){
        v[++ct]=V;
        x[ct]=al[U];
        al[U]=ct;
        w[ct]=va;
    }
    
    int main(){
        ll n,m,u1,v1,w1,ans=0,cnt=0;
        read(n);read(m);
        for(int i=1;i<=m;i++){
            read(u1);read(v1);read(w1);
            add(u1,v1,w1);add(v1,u1,w1);//
        }
        pq.push((data){0,1});
        while(!pq.empty()){
            ll u=pq.top().id;
            ans+=pq.top().dis;
            cnt++;
            visit[u]=true;
            pq.top();
            for(ll i=al[u];i;i=x[i]){
                if(!visit[v[i]])
                    pq.push((data){w[i],v[i]});
            }
            while(!pq.empty()&&visit[pq.top().id])
                pq.pop();
        }
        if(cnt<n){
        cout<<"orz"<<endl;
        return 0;
        }
        cout<<ans<<endl;
        return 0;
    }

    code2:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #define ll long long
    #define maxn 200010
    using namespace std;
    template<typename T>
    inline void read(T &x){
        x=0; bool flag=0; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') flag=1;
        for(;isdigit(c);c=getchar()) x=x*10+(c^48);
        if(flag) x=-x;
    }
    
    int n,m,x,y,z,t,ans=0,d[maxn],a[20010][20010];
    bool visit[maxn];
    
    void prim(){
        memset(d,0x3f,sizeof(d));
        d[1]=0;
        for(int i=1;i<=n;i++){
            t=0;
            for(int j=1;j<=n;j++)
                if((!visit[j])&&(t==0||d[j]<d[t]))
                    t=j;
            visit[t]=true;
            for(int s=1;s<=n;s++)
                if(!visit[s])
                    d[s]=min(d[s],a[t][s]);
        }
    }
    
    int main(){
        read(n);read(m);
        memset(a,0x3f,sizeof(a));
        for(int i=1;i<=n;i++) a[i][i]=0;
        for(int i=1;i<=m;i++){
            read(x),read(y),read(z);
            a[y][x]=a[x][y]=min(a[x][y],z);
        }
        prim();
        for(int i=2;i<=n;i++) ans+=d[i];
        cout<<ans<<endl;
        return 0;
    }

    虽然code2用邻接矩阵存看起来会比code1好懂亿点点,但是n和m稍微大点它就MLE了啊QAQ

    Boruvka/Sollin算法

    (以下内容来自课件)

    • 特化算法,用来针对一些奇怪的最小生成树问题

    • 1.找到距离每个点最近的点

    • 2.使用并查集维护连通性并缩点

    • 3.重复上述步骤,只不过这次对于每个子树找到离他最近的点

    • 由于每次循环联通块数目至少折半,因此复杂度是对的

    • 适用于解决最小异或生成树这样的棘手问题

  • 相关阅读:
    二分图 洛谷P2055 [ZJOI2009]假期的宿舍
    并查集 洛谷P1640 [SCOI2010]连续攻击游戏
    贪心 洛谷P2870 Best Cow Line, Gold
    贪心 NOIP2013 积木大赛
    快速幂 NOIP2013 转圈游戏
    倍增LCA NOIP2013 货车运输
    树形DP 洛谷P2014 选课
    KMP UVA1328 Period
    动态规划入门 BZOJ 1270 雷涛的小猫
    KMP POJ 2752Seek the Name, Seek the Fame
  • 原文地址:https://www.cnblogs.com/DReamLion/p/14387033.html
Copyright © 2011-2022 走看看