题目大意
给定一张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);
}