• 朱刘算法


    模板题传送门

    一个有向图,存在从某个点为根的,可以到达所有点的一个最小生成树,则它就是最小树形图。

    朱刘算法用来求最小树形图,复杂度O(nm)。

    大概思想就是缩点+贪心加边。

    如果图中存在环,就把环缩成一个点,一直到没有环为止。

    考虑正确性:

    最小树形图和最小生成树的区别就在与有方向,对于一个环,我们可以选择一条入边+所有环边-1(-1是因为有一条入边可以去掉一条环边)

    而对于每一个点,我们用一个minn数组存下了与它直接相连的点到它的最小边,ans等于所有minn的累加,那么一定是最优解。

    用while一直执行缩点过程。

    注意:vis用来避免一直走环。

    具体流程和解释代码中给出(借鉴洛谷题解)

    #include<bits/stdc++.h>
    #define N 103
    #define M 10002
    #define INF 210000001
    #define LL long long
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    struct EDGE{
        int a,b,v;
    }w[M];
    int minn[N],id[N],vis[N],fa[N];
    int cnt=0;
    //cnt当前图环的数量 
    //id[u]代表u节点在第id[u]个环中
    //vis[u]打标记避免一直走环 
    //minn[u]为当前连到u点的最短边的边权 fa[v]当前连到v点的最短边的u
    LL ans=0;
    int zhuliu(int n,int m,int r)
    {
        while(1)
        {
            for(int i=1;i<=n;++i) minn[i]=INF,id[i]=vis[i]=0;
            for(int i=1;i<=m;++i)
            {
                if(w[i].a!=w[i].b&&w[i].v<minn[w[i].b])//不是自环 并且边权比选定的还小 
                  fa[w[i].b]=w[i].a,minn[w[i].b]=w[i].v;
            }
            int u;
             minn[r]=0;
            for(int i=1;i<=n;++i)
            {
                if(minn[i]==INF)return 0;//存在一个不可以连接的点,什么不能找到最小树形图 
                ans+=minn[i];//这里就会更新到ans中 
                for(u=i;u!=r&&!id[u]&&vis[u]!=i;u=fa[u])vis[u]=i;//打上标记,这里走过的是一条链,vis打上i而不是1,因为可能多条边指向一个点 
                if(u!=r&&!id[u])//没有走到根,找到一个新环 
                {
                     id[u]=++cnt;
                    for(int v=fa[u];v!=u;v=fa[v])id[v]=cnt;
                 }
             }
            if(!cnt)return 1;//没有环了,说明现在就是最小树形图,边权和在上面就已经加入ans了 
            for(int i=1;i<=n;++i)
              if(!id[i])id[i]=++cnt;//i节点不存在当前树中 就给他自己成一个环 
            for(int i=1;i<=m;++i)
            {
                   int last=minn[w[i].b];//last等于当前连进v点的边的最小权值 
                if((w[i].a=id[w[i].a])!=(w[i].b=id[w[i].b]))w[i].v-=last;
                //缩环的时候记得加入答案,环上的出边直接接上,入边要注意,选一条入边相当于删掉一条环边
                //当前边的两个端点不在同一个环内
            }
            n=cnt;cnt=0;r=id[r];//缩完点后 当前点数就为环数 根节点就是根节点所在的环
        }
    }
    int main()
    {
        int n=read(),m=read(),r=read();
        for(int i=1;i<=m;++i)
          w[i].a=read(),w[i].b=read(),w[i].v=read();
        if(zhuliu(n,m,r))printf("%lld
    ",ans);
        else printf("-1
    ");
    }
    注释版

    再放一个没有注释的

    #include<bits/stdc++.h>
    #define N 103
    #define M 10002
    #define INF 210000001
    #define LL long long
    using namespace std;
    int read()
    {
        int x=0,f=1;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    struct EDGE{
        int a,b,v;
    }w[M];
    int minn[N],id[N],vis[N],fa[N];
    int cnt=0;
    LL ans=0;
    int zhuliu(int n,int m,int r)
    {
        while(1)
        {
            for(int i=1;i<=n;++i) minn[i]=INF,id[i]=vis[i]=0;
            for(int i=1;i<=m;++i)
            {
                if(w[i].a!=w[i].b&&w[i].v<minn[w[i].b])
                  fa[w[i].b]=w[i].a,minn[w[i].b]=w[i].v;
            }
            int u;
             minn[r]=0;
            for(int i=1;i<=n;++i)
            {
                if(minn[i]==INF)return 0;
                ans+=minn[i];
                for(u=i;u!=r&&!id[u]&&vis[u]!=i;u=fa[u])vis[u]=i;
                if(u!=r&&!id[u])
                {
                     id[u]=++cnt;
                    for(int v=fa[u];v!=u;v=fa[v])id[v]=cnt;
                 }
             }
            if(!cnt)return 1;
            for(int i=1;i<=n;++i)
              if(!id[i])id[i]=++cnt;
            for(int i=1;i<=m;++i)
            {
                   int last=minn[w[i].b];
                if((w[i].a=id[w[i].a])!=(w[i].b=id[w[i].b]))w[i].v-=last;
            }
            n=cnt;cnt=0;r=id[r];
        }
    }
    int main()
    {
        int n=read(),m=read(),r=read();
        for(int i=1;i<=m;++i)
          w[i].a=read(),w[i].b=read(),w[i].v=read();
        if(zhuliu(n,m,r))printf("%lld
    ",ans);
        else printf("-1
    ");
    }
    无注释版
  • 相关阅读:
    Java从入门到精通——调错篇之ORACLE 打开PLSQL时提示ora-01033
    2012年——2013年总结
    一个订单相关的存储过程(MySQL)
    EHCache 实现通用类 CacheManager
    超越算法来看待个性化推荐
    Jackson怎样转换这样的字符串? String jsonStr = "{dataType:'Custom',regexp:'t\d+',msg:'输入不正确'}";
    实时个性化推荐系统简述
    Java从入门到精通——基础篇之Servlet与JSP的区别
    TIME_WAIT引起Cannot assign requested address报错
    synchronized 用法,实例讲解
  • 原文地址:https://www.cnblogs.com/yyys-/p/11348556.html
Copyright © 2020-2023  润新知