BZOJ2547 CTSC2002玩具兵(最短路径+二分答案+最大流)
先不考虑只有一个显得有些特殊的天兵。
可以发现超能力的作用实质上是使兵更换职业。每一个兵到达某个位置最少需要更换职业的次数是彼此独立的,因为如果需要某两人互换职业可以使他们各自以当前职业到达需要到的地方,不会造成其中一个次数增加。
于是预处理出每个兵到达每个位置的最少代价。之后二分答案,把每个兵向可以到达的目标位置连边。跑最大流就可以知道是否可行。
最后考虑天兵。天兵可以任意游走,并且与天兵换职业的兵可以直接到达目的地。那么在最大流的结果上加上二分出的答案即可,因为每次使用超能力都可以送走一个兵。
1A爽爆。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 110 int n,m,k,l,dis[N][N][N],a[N][N],w[N][N],ans; struct pos{int x,y;}u[N],v[N]; namespace shortestpath { int d[N*N<<1],p[N*N<<1],t=0; int wx[4]={1,0,-1,0},wy[4]={0,1,0,-1}; bool flag[N*N<<1]; struct data{int to,nxt,len;}edge[N*N<<4]; int trans(int x,int y,int op){return op*n*m+(x-1)*m+y;} void addedge(int x,int y,int z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;} struct data2 { int x,d; bool operator <(const data2&a) const { return d>a.d; } }; priority_queue<data2> q; void make() { for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { for (int k=0;k<4;k++) if (i+wx[k]>0&&i+wx[k]<=n&&j+wy[k]>0&&j+wy[k]<=m) if (a[i][j]<a[i+wx[k]][j+wy[k]]) addedge(trans(i,j,0),trans(i+wx[k],j+wy[k],0),0), addedge(trans(i,j,1),trans(i+wx[k],j+wy[k],0),1); else if (a[i][j]>a[i+wx[k]][j+wy[k]]) addedge(trans(i,j,0),trans(i+wx[k],j+wy[k],1),1), addedge(trans(i,j,1),trans(i+wx[k],j+wy[k],1),0); else addedge(trans(i,j,0),trans(i+wx[k],j+wy[k],0),0), addedge(trans(i,j,1),trans(i+wx[k],j+wy[k],1),0); } } void dijkstra(int start) { memset(d,42,sizeof(d));d[trans(u[start].x,u[start].y,start>k)]=0; memset(flag,0,sizeof(flag)); while (!q.empty()) q.pop(); q.push((data2){trans(u[start].x,u[start].y,start>k),0}); for (int i=1;i<=(n*m<<1);i++) { while (!q.empty()&&flag[q.top().x]) q.pop(); if (q.empty()) break; data2 v=q.top();q.pop(); flag[v.x]=1; for (int j=p[v.x];j;j=edge[j].nxt) if (v.d+edge[j].len<d[edge[j].to]) { d[edge[j].to]=v.d+edge[j].len; q.push((data2){edge[j].to,d[edge[j].to]}); } } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) dis[start][i][j]=min(d[trans(i,j,0)],d[trans(i,j,1)]); } } namespace maxflow { const int S=0,T=201; int ans,p[N<<1],d[N<<1],q[N<<1],cur[N<<1],t; struct data{int to,nxt,cap,flow;}edge[N*N<<2]; void addedge(int x,int y,int z) { t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].cap=z,edge[t].flow=0,p[x]=t; t++;edge[t].to=x,edge[t].nxt=p[y],edge[t].cap=0,edge[t].flow=0,p[y]=t; } bool bfs() { memset(d,255,sizeof(d));d[S]=0; int head=0,tail=1;q[1]=S; do { int x=q[++head]; for (int i=p[x];~i;i=edge[i].nxt) if (d[edge[i].to]==-1&&edge[i].flow<edge[i].cap) { d[edge[i].to]=d[x]+1; q[++tail]=edge[i].to; } }while (head<tail); return ~d[T]; } int work(int k,int f) { if (k==T) return f; int used=0; for (int i=cur[k];~i;i=edge[i].nxt) if (d[k]+1==d[edge[i].to]) { int w=work(edge[i].to,min(edge[i].cap-edge[i].flow,f-used)); edge[i].flow+=w,edge[i^1].flow-=w; if (edge[i].flow<edge[i].cap) cur[k]=i; used+=w;if (used==f) return f; } if (used==0) d[k]=-1; return used; } void make(int lim) { t=-1;memset(p,255,sizeof(p)); for (int i=1;i<=(k<<1);i++) addedge(S,i,1); for (int i=1;i<=l;i++) addedge((k<<1)+i,T,w[v[i].x][v[i].y]); for (int i=1;i<=(k<<1);i++) for (int j=1;j<=l;j++) if (dis[i][v[j].x][v[j].y]<=lim) addedge(i,(k<<1)+j,1); } int dinic(int lim) { make(lim); ans=0; while (bfs()) { memcpy(cur,p,sizeof(p)); ans+=work(S,N); } return ans; } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2547.in","r",stdin); freopen("bzoj2547.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(),k=read(),l=read(); for (int i=1;i<=(k<<1|1);i++) u[i].x=read(),u[i].y=read(); for (int i=1;i<=l;i++) v[i].x=read(),v[i].y=read(),w[v[i].x][v[i].y]=read(); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) a[i][j]=read(); shortestpath::make(); for (int i=1;i<=(k<<1);i++) shortestpath::dijkstra(i); int l=0,r=k<<1,ans; while (l<=r) { int mid=l+r>>1; if (maxflow::dinic(mid)+mid>=(k<<1)) ans=mid,r=mid-1; else l=mid+1; } cout<<ans; return 0; }