最小树形图(朱刘算法)学习笔记&板子

最小树形图朱刘算法
大概流程:
初始化答案ans=0
1.每个点 v v 选个最小入边(u,v,prec[v]),如果有点没有 prec p r e c 就无解
2. i,irootans+=prec[i] ∑ i , i ≠ r o o t a n s + = p r e c [ i ]
2.若最小入边构成的是一棵树,那这个就是答案
3.否则最小入边会构成一棵树和若干个环,建一个新图 G G ′ ,在新图中把环缩成一个点 v v ′
4.设点 x x 在新图中对应点x,对于原图中的一条边 (u,v,w) ( u , v , w ) ,若他在新图中不是一个自环,建边 (u,v,wprec[v]) ( u ′ , v ′ , w − p r e c [ v ] ) ,然后回到步骤1

板子
hdu2121
code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define pb push_back
#define SZ(x) (int)x.size()
#define mp make_pair
#define fir first
#define sec second
using namespace std;

const int maxn = 11000;
const int maxm = 11000;

ll ans,sum; int root;
int n,m;
struct node
{
    int ty,y; ll c;
    node(){}
    node(int _ty,int _y,ll _c){ty=_ty,y=_y,c=_c;}
};
vector<node>G[maxn],nG[maxn];
ll pc[maxn];
int pi[maxn],pv[maxn];

void init()
{
    ans=0,sum=0;
    for(int i=1;i<=n+1;i++) G[i].clear(),nG[i].clear(),pc[i]=LLONG_MAX,pv[i]=0,pi[i]=0;
}
int id[maxn],cnt;
int vis[maxn];
int solve(int rt)
{
    while(1)
    {
        for(int i=1;i<=n;i++) if(i!=rt&&!pv[i]) return 0;
        for(int i=1;i<=n;i++) ans+=pc[i];

        int cir=0; cnt=0;
        for(int i=1;i<=n;i++) vis[i]=0,id[i]=0;
        vis[rt]=-1;
        for(int i=1;i<=n;i++)
        {
            int u;
            for(u=i;!vis[u];u=pv[u]) vis[u]=i;
            if(vis[u]==i)
            {
                cir=1;
                id[u]=++cnt;
                for(int v=pv[u];v!=u;v=pv[v]) id[v]=cnt;
            }
        }
        for(int i=1;i<=n;i++) if(!id[i]) id[i]=++cnt;
        if(!cir)
        {
            for(int i=1;i<=n;i++) if(i!=rt&&pv[i]==rt) root=pi[i];
            return ans<2*sum;
        }

        for(int i=1;i<=cnt;i++) nG[i].clear();
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<SZ(G[i]);j++)
            {
                int y=G[i][j].y; ll c=G[i][j].c-pc[y];
                if(id[i]==id[y]) continue;
                nG[id[i]].pb(node(G[i][j].ty,id[y],c));
            }
        }
        n=cnt;
        for(int i=1;i<=n;i++) pc[i]=LLONG_MAX,pi[i]=pv[i]=0;
        for(int i=1;i<=n;i++)
        {
            G[i].clear();
            for(int j=0;j<SZ(nG[i]);j++)
            {
                G[i].pb(nG[i][j]);
                int y=G[i][j].y; ll c=G[i][j].c;
                if(c<pc[y]||(c==pc[y]&&G[i][j].ty<pi[y]))
                    pc[y]=c,pv[y]=i,pi[y]=G[i][j].ty;
            }
        }
        pc[rt=id[rt]]=0;
    }
}

int main()
{
    //freopen("tmp.in","r",stdin);
    //freopen("tmp.out","w",stdout);

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=1;i<=m;i++)
        {
            int x,y;ll c; scanf("%d%d%lld",&x,&y,&c);
            x++,y++;
            if(x==y) continue;
            G[x].pb(node(y,y,c));
            sum+=c;
            if(pc[y]>c) pc[y]=c,pv[y]=x,pi[y]=y;
        }
        sum++;
        for(int i=1;i<=n;i++)
        {
            G[n+1].pb(node(i,i,sum));
            if(pc[i]>sum) pc[i]=sum,pv[i]=n+1,pi[i]=i;
        }
        pc[++n]=0;
        if(!solve(n)) puts("impossible");
        else printf("%lld %d\n",ans-sum,root-1);
        putchar('\n');
    }

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值