[bzoj2324][ZJOI2011]营救皮卡丘
来自FallDream的博客,未经允许,请勿转载,谢谢。
皮卡丘被火箭队用邪恶的计谋抢走了!这三个坏家伙还给小智留下了赤果果的挑衅!为了皮卡丘,也为了正义,小智和他的朋友们义不容辞的踏上了营救皮卡丘的道路。
火箭队一共有N个据点,据点之间存在M条双向道路。据点分别从1到N标号。小智一行K人从真新镇出发,营救被困在N号据点的皮卡丘。为了方便起见,我们将真新镇视为0号据点,一开始K个人都在0号点。
由于火箭队的重重布防,要想摧毁K号据点,必须按照顺序先摧毁1到K-1号据点,并且,如果K-1号据点没有被摧毁,由于防御的连锁性,小智一行任何一个人进入据点K,都会被发现,并产生严重后果。因此,在K-1号据点被摧毁之前,任何人是不能够经过K号据点的。
为了简化问题,我们忽略战斗环节,小智一行任何一个人经过K号据点即认为K号据点被摧毁。被摧毁的据点依然是可以被经过的。
K个人是可以分头行动的,只要有任何一个人在K-1号据点被摧毁之后,经过K号据点,K号据点就被摧毁了。显然的,只要N号据点被摧毁,皮卡丘就得救了。
野外的道路是不安全的,因此小智一行希望在摧毁N号据点救出皮卡丘的同时,使得K个人所经过的道路的长度总和最少。
请你帮助小智设计一个最佳的营救方案吧! n<=150 k<=10
首先这个数据范围显然是费用流qaq
然后自己yy了一个建图,把原图拆点,强制他们之间的边流1,然后从S向0连k的边,向每个点的出点连1的边,直接把原图的边扔进去了,轻松wa题,想了想貌似没法保证它们走的顺序....
所以考虑把两点之间可行的最短路径直接求出来,然后建边,就能保证结果合法了。也就是对于i->j的路径,强制只走小等于j的点,floyd求出之后从i的出点向j的入点连费用是dis[i][j]的边就行了。
嗯这个强制流1其实就是从一个点的入点向T连流量为1的边,从S向出点连流量为1的边,满足最大流的时候一定满流。如果还不是很清楚可以学习一下带上下界网络流的一套理论。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define ll long long #define S 0 #define T 303 #define INF 1000000000 using namespace std; inline ll read() { ll x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int n,m,k,cnt=1,head[T+5],ans=0,pi=0,d[T+5],dis[155][155]; bool mark[T+5],inq[T+5]; deque<int> q; struct edge{int to,next,w,c;}e[80005]; inline void ins(int f,int t,int w,int c) { e[++cnt]=(edge){t,head[f],w,c}; head[f]=cnt; e[++cnt]=(edge){f,head[t],0,-c};head[t]=cnt; } bool modlabel() { q.push_back(T); for(int i=S;i<T;i++)d[i]=INF;d[T]=0;inq[T]=1; while(!q.empty()) { int x=q.front();q.pop_front(); for(int i=head[x];i;i=e[i].next) if(e[i^1].w&&d[x]+e[i^1].c<d[e[i].to]) { d[e[i].to]=d[x]+e[i^1].c; if(!inq[e[i].to]) { inq[e[i].to]=1; if(d[e[i].to]<d[q.size()?q.front():0]) q.push_front(e[i].to); else q.push_back(e[i].to); } } inq[x]=0; } for(int i=S;i<=T;i++) for(int j=head[i];j;j=e[j].next) e[j].c+=d[e[j].to]-d[i]; return pi+=d[S],d[S]<INF; } int dfs(int x,int f) { if(x==T) return ans+=pi*f,f; int used=0;mark[x]=1; for(int i=head[x];i;i=e[i].next) if(e[i].w&&!e[i].c&&!mark[e[i].to]) { int w=dfs(e[i].to,min(f-used,e[i].w)); used+=w;e[i].w-=w;e[i^1].w+=w; if(f==used) return f; } return used; } int main() { n=read()+1;m=read();k=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j) dis[i][j]=INF; ins(S,1,k,0); for(int i=2;i<=n;i++) ins(i+n,T,1,0),ins(S,i,1,0); for(int i=1;i<=m;i++) { int x=read()+1,y=read()+1,c=read(); dis[x][y]=min(dis[x][y],c); dis[y][x]=min(dis[y][x],c); } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(dis[i][k]+dis[k][j]<dis[i][j]&&max(i,j)>=k) dis[i][j]=dis[i][k]+dis[k][j]; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(dis[i][j]<INF) ins(i,j+n,INF,dis[i][j]); while(modlabel()) do memset(mark,0,sizeof(mark)); while(dfs(S,INF)); printf("%d\n",ans); return 0; }