bzoj1927: [Sdoi2010]星际竞速

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1927

思路:拆点拆成x和x'

S向x'连边,容量为1,费用为定位时间

S向x连边,容量为1,费用为0

对于原图的边u->v

u向v‘连边,容量为1,费用为时间

x’向T连边,容量为1,费用为0;

跑一遍费用流即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=1610,maxm=200010;
using namespace std;
int n,m,A[maxn],pre[maxm],now[maxn],son[maxm],val[maxm],cost[maxm],from[maxm],tot=1;
int S=maxn-2,T=maxn-1,dis[maxn],q[maxm+10],head,tail,last[maxn],minc,flow;
bool bo[maxn];
struct Edge{int x,y,z;}E[maxm];
void add(int a,int b,int c,int d){pre[++tot]=now[a],now[a]=tot,from[tot]=a,son[tot]=b,val[tot]=c,cost[tot]=d;}
void ins(int a,int b,int c,int d){add(a,b,c,d),add(b,a,0,-d);}
int p0(int x){return x<<1;}
int p1(int x){return (x<<1)|1;}

bool spfa(){
	memset(dis,63,sizeof(dis));int inf=dis[0];
	memset(bo,0,sizeof(bo));
	q[tail=1]=S,dis[S]=head=0;
	while (head!=tail){
		if (++head>maxm) head=1;
		int x=q[head];
		for (int y=now[x];y;y=pre[y])
			if (val[y]>0&&dis[son[y]]>dis[x]+cost[y]){
				dis[son[y]]=dis[x]+cost[y],last[son[y]]=y;
				if (!bo[son[y]]){
					if (++tail>maxm) tail=1;
					q[tail]=son[y],bo[son[y]]=1;
				}
			}
		bo[x]=0;
	}
	return dis[T]<inf;
}

void find(){
	int lim=1e9;
	for (int x=T,y;x!=S;x=from[y]) y=last[x],lim=min(lim,val[y]);
	flow+=lim;
	for (int x=T,y;x!=S;x=from[y]) y=last[x],minc+=lim*cost[y],val[y]-=lim,val[y^1]+=lim;
}

int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",&A[i]);
	for (int i=1,x,y,z;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		if (x>y) swap(x,y);
		E[i]=(Edge){x,y,z};
	}
	for (int i=1;i<=n;i++) ins(S,p1(i),1,A[i]),ins(S,p0(i),1,0),ins(p1(i),T,1,0);
	for (int i=1;i<=m;i++) ins(p0(E[i].x),p1(E[i].y),1,E[i].z);
	while (spfa()) find();
	//spfa();
	//for (int i=1;i<=n;i++) printf("%d
",from[last[i]]);
	printf("%d
",minc);
	return 0;
}


原文地址:https://www.cnblogs.com/thythy/p/5493458.html