[SDOI2010][bzoj1927] 星际竞速 [最小路径覆盖+费用流]

题面

传送门

思路

仔细观察题目要求的东西,发现就是求一个最小路径覆盖,只不过可以跳跃(就是那个鬼畜的超级跳跃)

那么就直接上最小路径覆盖模版

对每个点,拆成两个点$X_i$和$Y_i$,建立超级源超级汇S,T

连边$\left(S,X_i\right)$,$\left(Y_i,T\right)$,流量1费用0

对于原图中的边$\left(i,j\right)$,连边$\left(X_i,Y_j\right)$,流量1费用为原本的时间

对于超级跳跃,连边$\left(S,Y_i\right)$,流量1费用为跳跃时间

跑S-T最小费用最大流即可

Code

在luogu+COGS上莫名其妙T了

但是bzoj2400ms过

interesting......

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 1e9
using namespace std;
inline int read(){
	int re=0,flag=1;char ch=getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-') flag=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
	return re*flag;
}
int first[2010],dis[2010],vis[2010],n,m,cnt=-1,ans;
struct edge{
	int to,next,w,cap;
}a[150010];
inline void add(int u,int v,int w,int cap){
	a[++cnt]=(edge){v,first[u],w,cap};first[u]=cnt;
	a[++cnt]=(edge){u,first[v],-w,0};first[v]=cnt;
}
int q[100010];
bool spfa(int s,int t){
	int head=0,tail=1,i,u,v,w;
	memset(dis,-1,sizeof(dis));memset(vis,0,sizeof(vis));
	q[0]=t;dis[t]=0;vis[t]=1;
	while(head<tail){
		u=q[head++];vis[u]=0;
		for(i=first[u];~i;i=a[i].next){
			v=a[i].to;w=a[i].w;
			if(a[i^1].cap&&((dis[v]==-1)||(dis[v]>dis[u]-w))){
				dis[v]=dis[u]-w;
				if(!vis[v]) q[tail++]=v,vis[v]=1;
			}
		}
	}
	return ~dis[s];
}
int dfs(int u,int t,int limit){
	if((u==t)||(!limit)){vis[u]=1;return limit;}
	int i,v,f,flow=0,w;vis[u]=1;
	for(i=first[u];~i;i=a[i].next){
		v=a[i].to;w=a[i].w;
		if(dis[v]==dis[u]-w&&a[i].cap&&!vis[v]){
			if(!(f=dfs(v,t,min(limit,a[i].cap)))) continue;
			a[i].cap-=f;a[i^1].cap+=f;
			ans+=f*w;flow+=f;limit-=f;
			if(!limit) return flow;
		}
	}
	return flow;
}
int zkw(int s,int t){
	int re=0;
	while(spfa(s,t)){
		vis[t]=1;
		while(vis[t]){
			memset(vis,0,sizeof(vis));
			re+=dfs(s,t,inf);
		}
	}
	return re;
}
int jump[1010];
int main(){
	freopen("starrace8.in","r",stdin);
	freopen("starrace.out","w",stdout);
	memset(first,-1,sizeof(first));
	n=read();m=read();int i,t1,t2,t3;
	for(i=1;i<=n;i++) jump[i]=read(),add(0,i+n,jump[i],1);
	for(i=1;i<=m;i++){
		t1=read();t2=read();t3=read();
		if(t1>t2) swap(t1,t2);
		add(t1,t2+n,t3,1);
	}
	for(i=1;i<=n;i++) add(0,i,0,1),add(i+n,(n<<1)+1,0,1);
	zkw(0,(n<<1)+1);
	printf("%d\n",ans);
}
posted @ 2018-04-08 10:17  dedicatus545  阅读(175)  评论(0编辑  收藏  举报