在lrj的某一本书(蓝书?紫书?黑书?我不知道>_<)里面看到过。
首先可以把交换看成是更改职业,那么二分答案x,我们就得到了每个玩具兵(不包括天兵)可以到达的目标格。那么连一条边就得到一个二分图,跑最大匹配即可。
注意不能直接判断是否有完美匹配,还要考虑天兵,因为我们可以让天兵到x个地方然后作x次交换,那么在跑最大匹配的时候不把天兵加进去,然后判断是否有最大匹配数+x>=n即可。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<algorithm> #define N 115 #define M 1005 using namespace std; const int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1}; int m,n,cnt,pt,tot,c[N][N],d[N][N],path[N],mp[N][N]; struct node{ int x,y; }a[N],b[N],h[M+5]; bool bo[N],inq[N][N]; void bfs(int sx,int sy,int p){ memset(d,0x3f,sizeof(d)); d[sx][sy]=0; int i,t,head=0,tail=1; node u,v; h[1].x=sx; h[1].y=sy; memset(inq,0,sizeof(inq)); while (head!=tail){ head=head%M+1; u=h[head]; inq[u.x][u.y]=0; for (i=0; i<4; i++){ v.x=u.x+dx[i]; v.y=u.y+dy[i]; if (v.x<=0 || v.x>m || v.y<=0 || v.y>n) continue; if (p^(d[u.x][u.y]&1)) if (c[v.x][v.y]<=c[u.x][u.y]) t=0; else t=1; else if (c[v.x][v.y]>=c[u.x][u.y]) t=0; else t=1; if (d[u.x][u.y]+t<d[v.x][v.y]){ d[v.x][v.y]=d[u.x][u.y]+t; if (!inq[v.x][v.y]){ inq[v.x][v.y]=1; tail=tail%M+1; h[tail]=v; } } } } } bool dfs(int x,int lim){ int i; for (i=1; i<=tot; i++) if (bo[i] && mp[x][i]<=lim){ bo[i]=0; if (!path[i] || dfs(path[i],lim)){ path[i]=x; return 1; } } return 0; } bool ok(int x){ int i,tmp=0; memset(path,0,sizeof(path)); for (i=1; i<=(cnt<<1); i++){ memset(bo,1,sizeof(bo)); if (dfs(i,x)) tmp++; } return tmp+x>=(cnt<<1); } int main(){ scanf("%d%d%d%d",&m,&n,&cnt,&pt); int i,j,x,y,t; for (i=1; i<=(cnt<<1|1); i++) scanf("%d%d",&a[i].x,&a[i].y); for (i=1; i<=pt; i++){ scanf("%d%d%d",&x,&y,&t); while (t--){ b[++tot].x=x; b[tot].y=y; } } for (i=1; i<=m; i++) for (j=1; j<=n; j++) scanf("%d",&c[i][j]); for (i=1; i<=(cnt<<1); i++){ if (i<=cnt) bfs(a[i].x,a[i].y,0); else bfs(a[i].x,a[i].y,1); for (j=1; j<=tot; j++) mp[i][j]=d[b[j].x][b[j].y]; } int l=0,r=cnt<<1,mid; while (l<r){ mid=(l+r)>>1; if (ok(mid)) r=mid; else l=mid+1; } printf("%d\n",l); return 0; }
by lych
2016.4.6