BZOJ3876: [Ahoi2014]支线剧情
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3876
最小费用最小流。
图是一个拓扑图并要求每条边都要至少走一次求最小花费。
比如说u连出了k条边,其中一条连向v,费用为w,就u→T(k,0) u→v(inf,w) S→v(1,w) 最后 u→1(inf,0)表示不以1为源点。
然后跑一遍费用流就可以了。
#include<cstring> #include<iostream> #include<cstdio> #include<queue> #include<cmath> #include<algorithm> #define rep(i,l,r) for (int i=l;i<=r;i++) #define down(i,l,r) for (int i=l;i>=r;i--) #define clr(x,y) memset(x,y,sizeof(x)) #define low(x) (x&(-x)) #define maxn 505 #define inf int(1e9) #define mm 1000000007 #define ll long long using namespace std; int tot=1,n,t,ans; int head[maxn],dis[maxn],uu[maxn],cur[maxn]; struct data{int obj,pre,c,w; }e[500500]; int read(){ int x=0,f=1; char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} return x*f; } void insert(int x,int y,int c,int w){ e[++tot].obj=y; e[tot].pre=head[x]; e[tot].c=c; e[tot].w=w; head[x]=tot; e[++tot].obj=x; e[tot].pre=head[y]; e[tot].c=0; e[tot].w=-w; head[y]=tot; } bool spfa(){ clr(uu,0); rep(i,0,t) dis[i]=inf; dis[0]=0; queue<int> q; q.push(0); while (!q.empty()){ int u=q.front(); q.pop(); uu[u]=1; for (int j=head[u];j;j=e[j].pre){ int v=e[j].obj; if (e[j].c>0&&dis[v]>dis[u]+e[j].w){ dis[v]=dis[u]+e[j].w; if (uu[v]==0) uu[v]=1,q.push(v); } } uu[u]=0; } if (dis[t]>=inf) return 0; return 1; } int dfs(int x,int mx){ //if (uu[x]==1) return 0; if (x==t||mx==0) return mx; uu[x]=1; int used=0; for (int j=cur[x];j;j=e[j].pre){ int v=e[j].obj; if (dis[v]==dis[x]+e[j].w&&e[j].c>0&&uu[v]==0){ int w=dfs(v,min(e[j].c,mx-used)); ans+=w*e[j].w; e[j].c-=w; e[j^1].c+=w; used+=w; cur[x]=j; if (used==mx) return used; } } return used; } int main(){ n=read(); t=n+1; rep(i,1,n){ int k=read(); insert(i,1,inf,0); insert(i,t,k,0); rep(j,1,k){ int x=read(), y=read(); insert(i,x,inf,y); insert(0,x,1,y); } } while (spfa()){ clr(uu,0); rep(i,0,t) cur[i]=head[i]; dfs(0,inf); } printf("%d\n",ans); return 0; }