【bzoj1927】星际竞速 费用流

       据说这是最小路径覆盖问题??反正我这么弱是不知道。

       实际上,由于边是有方向的,所以如果对于任意一个点,只要入度=1,出度=1,就一定能保证满足题意。那么,我们只需要让每个点保证它去向的点只有1个,然后考虑它的前驱即可。

       那么,如果令x表示该点是出发向其它的,x'表示到达的,则构图如下:

1.瞬移:从S->x',流量为1,费用为瞬移的花费;

2.航道x->y:从x->y',流量为1,费用为航道花费;

3.显然任意x必须要有流量才能出发,且只能出发去一个地方,故:S->x,流量为1,费用为0;

4.显然任意x'的入度为1,但上述条件无法保证,古:x'->T,流量为1,费用为0;

       这样任意最大流都是一个合法的方案,最有解即最小费用最大流。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 60005
#define inf 1000000000
using namespace std;

int n,m,tot=1,sta,ans,gol,fst[N],pnt[N],len[N],cst[N],nxt[N],d[N],e[N],h[N]; bool vis[N];
void add(int aa,int bb,int cc,int dd){
	pnt[++tot]=bb; len[tot]=cc; cst[tot]=dd; nxt[tot]=fst[aa]; fst[aa]=tot;
}
bool spfa(){
	memset(vis,0,sizeof(vis)); int i;
	for (i=sta; i<=gol; i++) d[i]=inf; d[sta]=0;
	int head=0,tail=1; h[1]=sta;
	while (head<tail){
		head=head%(N-5)+1; int x=h[head],p; vis[x]=0;
		for (p=fst[x]; p; p=nxt[p]) if (len[p]){
			int y=pnt[p];
			if (d[x]+cst[p]<d[y]){
				d[y]=d[x]+cst[p];  if (vis[y]) continue;
				vis[y]=0; tail=tail%(N-5)+1; h[tail]=y; e[y]=p;
			}
		}
	}
	return d[gol]<inf;
}
void updata(){
	int i,tmp=inf;
	for (i=gol; i!=sta; i=pnt[e[i]^1]) tmp=min(tmp,len[e[i]]);
	ans+=tmp*d[gol];
	for (i=gol; i!=sta; i=pnt[e[i]^1]){
		len[e[i]]-=tmp; len[e[i]^1]+=tmp;
	}
}
int main(){
	scanf("%d%d",&n,&m); int i;
	sta=0; gol=n<<1|1;
	for (i=1; i<=n; i++){
		int x; scanf("%d",&x); add(0,i+n,1,x); add(i+n,0,0,-x);
		add(0,i,1,0); add(i,0,0,0); add(i+n,gol,1,0); add(gol,i+n,0,0);
	}
	for (i=1; i<=m; i++){
		int x,y,z; scanf("%d%d%d",&x,&y,&z);
		if (x>y) swap(x,y); add(x,y+n,1,z); add(y+n,x,0,-z);
	}
	while (spfa()) updata(); printf("%d\n",ans);
	return 0;
}

by lych

2016.2.3

你可能感兴趣的:(最小费用最大流)