BZOJ 1927: [Sdoi2010]星际竞速(费用流)

传送门

解题思路

  仿照最小路径覆盖问题,用费用流解决此题。最小路径覆盖问题是拆点连边后用\(n-\)最大匹配,这里的话也是将每个点拆点,源点向入点连流量为\(1\),费用为\(0\)的边,向出点连流量为\(1\),费用为\(a[i]\)的边,出点向汇点连流量为\(1\),费用为\(0\)的边。然后对于每条边,由\(x\)的入点向\(y\)的出点连流量为\(1\),费用为路径长度的边。跑一遍费用流。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;
const int MAXN = 1605;
const int MAXM = 20005;
const int inf = 0x3f3f3f3f;
typedef long long LL;

inline int rd(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return f?x:-x;
}

inline int min(int x,int y){
	return x<y?x:y;
}

int n,m,a[MAXN],head[MAXN],to[MAXM<<1],nxt[MAXM<<1],val[MAXM<<1],cost[MAXM<<1];
int S,T,cnt=1,incf[MAXN],pre[MAXN],dis[MAXN];
LL ans;
bool vis[MAXN];
queue<int> Q;

inline void add(int bg,int ed,int w,int z){
	to[++cnt]=ed,nxt[cnt]=head[bg],val[cnt]=w,cost[cnt]=z,head[bg]=cnt;
}

inline bool spfa(){
	memset(dis,0x3f,sizeof(dis));
	memset(vis,false,sizeof(vis));
	while(Q.size()) Q.pop();
	dis[S]=0;vis[S]=1;Q.push(S);incf[S]=inf;
	while(Q.size()){
		int x=Q.front();Q.pop();vis[x]=0;
		for(int i=head[x];i;i=nxt[i]){
			int u=to[i];
			if(dis[x]+cost[i]<dis[u] && val[i]){
				dis[u]=dis[x]+cost[i];
				incf[u]=min(incf[x],val[i]);
				pre[u]=i;
				if(!vis[u]) vis[u]=1,Q.push(u);
			}
		}
	}
	return (dis[T]==inf)?0:1;	
}

inline void update(){
	int x=T,i;
	while(x!=S){
		i=pre[x];
		val[i]-=incf[T];
		val[i^1]+=incf[T];
		x=to[i^1];
	}
	ans+=(LL)incf[T]*dis[T];
}

int main(){
	n=rd(),m=rd();int x,y,z;S=2*n+1,T=2*n+2;
	for(int i=1;i<=n;i++) a[i]=rd();
	for(int i=1;i<=n;i++){
		add(S,i,1,0),add(i,S,0,0);
		add(S,i+n,1,a[i]),add(i+n,S,0,-a[i]);
		add(i+n,T,1,0),add(T,i+n,0,0);
	}
	for(int i=1;i<=m;i++){
		x=rd(),y=rd(),z=rd();if(x>y) swap(x,y);
		add(x,y+n,1,z),add(y+n,x,0,-z);
	}
	while(spfa()) update();
	printf("%lld\n",ans);
	return 0;
}
posted @ 2018-12-14 10:19  Monster_Qi  阅读(121)  评论(0编辑  收藏  举报