[bzoj3876]支线剧情

题目大意

给定一张DAG,1是唯一一个入度为0的点。每条边都有经过费用,你每次可以从1走到任意节点,每条边需要经过至少一次求最小费用。

最小费用可行流

建立s和t,然后s向1连下限0上限inf费用0的边,除1外所有节点向t连下限0上限inf费用0的边,对于每条边下限为1上限为inf费用为经过费用,然后我们只有做上下界网络流构出新图,跑最小费用可行流即可。

参考程序

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=300+10,maxm=(5000+300)*2*3+20,inf=1000000000;
int h[maxn],now[maxn],d[maxn],go[maxm],dis[maxm],co[maxm],next[maxm],fx[maxm];
bool bz[maxn];
int i,j,k,l,r,s,t,ss,tt,n,m,tot,ans;
void add(int x,int y,int z,int c,int d){
    go[++tot]=y;
    dis[tot]=z;
    co[tot]=c;
    fx[tot]=tot+d;
    next[tot]=h[x];
    h[x]=tot;
}
void link(int x,int y,int l,int r,int a){
    add(ss,y,l,a,1);
    add(y,ss,0,-a,-1);
    add(x,tt,l,0,1);
    add(tt,x,0,0,-1);
    add(x,y,r-l,a,1);
    add(y,x,0,-a,-1);
}
int dfs(int x,int cost){
    bz[x]=1;
    if (x==tt){
        ans+=cost;
        return 1;
    }
    int r=now[x];
    while (r){
        if (!bz[go[r]]&&dis[r]&&d[x]==d[go[r]]+co[r]){
            if (dfs(go[r],cost+co[r])){
                dis[r]--;
                dis[fx[r]]++;
                now[x]=r;
                return 1;
            }
        }
        r=next[r];
    }
    now[x]=0;
    return 0;
}
bool change(){
    int tmp=inf,i,r;
    fo(i,ss,tt)
        if (bz[i]){
            r=h[i];
            while (r){
                if (!bz[go[r]]&&dis[r]&&d[go[r]]+co[r]-d[i]<tmp) tmp=d[go[r]]+co[r]-d[i];
                r=next[r];
            }
        }
    if (tmp==inf) return 0;
    fo(i,ss,tt) 
        if (bz[i]) d[i]+=tmp;
    return 1;
}
int main(){
    freopen("story.in","r",stdin);freopen("story.out","w",stdout);;
    scanf("%d",&n);
    s=2;t=n+3;
    ss=1;tt=n+4;
    fo(i,1,n){
        scanf("%d",&l);
        while (l--){
            scanf("%d%d",&j,&k);
            link(i+2,j+2,1,inf,k);
        }
    }
    link(s,3,0,inf,0);
    fo(i,2,n)
        link(i+2,t,0,inf,0);
    link(t,s,0,inf,0);
    do{
        fo(i,ss,tt) now[i]=h[i];
        fill(bz+ss,bz+tt+1,0);
        while (dfs(ss,0)) fill(bz+ss,bz+tt+1,0);
    }while (change());
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值