最小樹形圖(朱劉演算法)

mfy的一號小迷弟發表於2020-12-12

洛谷P4716 【模板】最小樹形圖

題意:

n個點m條邊得有向圖,以s為根的最小樹形圖,及除s外其他點的入度為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;
//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");
}

相關文章