一個有向圖,存在從某個點為根的,可以到達所有點的一個最小生成樹,則它就是最小樹形圖。
朱劉算法用來求最小樹形圖,復雜度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"); }