【bzoj3876】 Ahoi2014—支线剧情

http://www.lydsy.com/JudgeOnline/problem.php?id=3876 (题目链接)

题意

  给出一张拓扑图,每条边有一个权值,问每次从1号点出发,走遍所有的边所需要的最小花费是多少。

Solution

  上下界最小费用可行流。

  因为每条边至少要被经过一次,所以每条边有个流量下界1。又因为每个节点都可以作为结束点,那么每个节点都有可能成为汇点,所以每个点都要向源点1连一条容量为无穷费用为0的边。

  这样套上上下界网络流的模型,求解费用最小的可行流即可。

细节

  边数$n^2$

代码

// bzoj3876
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf (1ll<<30)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;

const int maxn=1010;
int cnt=1,n,head[maxn],S,T,ans;
int vis[maxn],dis[maxn],f[maxn],p[maxn];

struct edge {int from,to,next,w,c;}e[maxn*maxn];
									 
void link(int u,int v,int w,int c) {
	e[++cnt]=(edge){u,v,head[u],w,c};head[u]=cnt;
	e[++cnt]=(edge){v,u,head[v],0,-c};head[v]=cnt;
}
bool SPFA() {
	for (int i=S;i<=T;i++) dis[i]=inf;
	queue<int> q;q.push(S);dis[S]=0;f[S]=inf;
	while (!q.empty()) {
		int x=q.front();q.pop();
		vis[x]=0;
		for (int i=head[x];i;i=e[i].next) if (e[i].w && dis[e[i].to]>dis[x]+e[i].c) {
				dis[e[i].to]=dis[x]+e[i].c;
				f[e[i].to]=min(f[x],e[i].w);
				p[e[i].to]=i;
				if (!vis[e[i].to]) q.push(e[i].to),vis[e[i].to]=1;
			}
	}
	if (dis[T]==inf) return 0;
	for (int i=p[T];i;i=p[e[i].from]) e[i].w-=f[T],e[i^1].w+=f[T];
	ans+=dis[T]*f[T];
	return 1;
}
int main() {
	scanf("%d",&n);
	S=0,T=n+1;
	for (int k,i=1;i<=n;i++) {
		scanf("%d",&k);
		for (int x,y,j=1;j<=k;j++) {
			scanf("%d%d",&x,&y);
			link(i,x,inf,y);
			link(S,x,1,y);
		}
		link(i,T,k,0);
		if (i!=1) link(i,1,inf,0);
	}
	while (SPFA());
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/MashiroSky/p/6677970.html