朱劉算法


模板題傳送門

一個有向圖,存在從某個點為根的,可以到達所有點的一個最小生成樹,則它就是最小樹形圖。

朱劉算法用來求最小樹形圖,復雜度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\n",ans);
    else printf("-1\n");
}
注釋版

再放一個沒有注釋的

#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\n",ans);
    else printf("-1\n");
}
無注釋版

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM