最小树形图-朱刘算法

朱刘算法

最小树形图

即:有向图中的最小生成树(外向树)

大致思路

1.对每个点求出边权最小的入边,并记录
2.看1,中组成的图有无环,且能否组成树
3.若存在环,则将环缩成一个点,重新赋边权,回到1.

代码

(洛谷模板)

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define get getchar()
#define in inline
in int read()
{
    int t=0; char ch=get;
    while(ch<'0' || ch>'9') ch=get;
    while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
    return t;
}
const int _=1e4+23;
const int inf=0x3f3f3f3f;
int  rt, n, m;
struct E{
    int u, v, w;
}ed[_];
int id[_], mn[_], pre[_], vis[_];
ll ans=0;
in void zhuliu(int n)
{
    while(1)
    {
        int cnt=0;
        for(re int i=1;i<=n;++i) mn[i]=inf;
        for(re int i=1;i<=m;++i) //找到最小入边
            if(ed[i].w<mn[ed[i].v] && ed[i].u!=ed[i].v)
                mn[ed[i].v]=ed[i].w, pre[ed[i].v]=ed[i].u;
        for(re int i=1;i<=n;++i) if(i!=rt && mn[i]==inf) {ans=-1;return ;} //判断是否能组成外向树
        for(re int i=1;i<=n;++i) vis[i]=id[i]=0;
        for(re int i=1;i<=n;++i)
        {
            if(i==rt) continue;
            ans+=mn[i];
            int v=i;
            while(vis[v]!=i && !id[v] && v!=rt) vis[v]=i, v=pre[v]; //找环
            if(!id[v] && v!=rt) //把环缩点
            {
                id[v]=++cnt;
                for(re int u=pre[v];u!=v;u=pre[u]) id[u]=cnt;
            }
        }
        if(cnt==0) break;
        for(re int i=1;i<=n;++i) if(!id[i]) id[i]=++cnt;
        for(re int i=1;i<=m;++i) //重新赋边权与标号
        {
            int u=ed[i].u, v=ed[i].v;
            ed[i].u=id[u], ed[i].v=id[v];
            if(ed[i].u!=ed[i].v) ed[i].w-=mn[v]; 
        }
        rt=id[rt], n=cnt;
    }
    return ;
}
int main()
{
    n=read(), m=read(), rt=read();
    for(re int i=1;i<=m;++i) ed[i].u=read(), ed[i].v=read(), ed[i].w=read();
    zhuliu(n);
    printf("%lld\n", ans);
    return 0;
}
posted @ 2021-09-10 22:23  yzhx  阅读(107)  评论(0编辑  收藏  举报