BZOJ 2547: [Ctsc2002]玩具兵(二分答案+二分图匹配)
解题思路
可以发现天兵不用管,答案的一个上界是\(2*k\),就是天兵一个个换。刚开始写了个拆\(6\)点的网络流,调了半天发现自己假了。。说说正解,首先可以发现交换士兵其实就是种类的交换,那么可以对于每一个点算出它距离所有点的距离,距离就是最少换几次,这个可以\(spfa\)求出,之后发现这个很像二分图匹配,可以二分答案表示用几次超能力,然后跑二分图匹配即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=105;
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;
}
int n,m,k,T,val[N][N],dis[N][N],xx[N],yy[N],match[N];
int h[N][N],tx[N],ty[N],dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},ans,tot;
bool vis[N][N],use[N];
queue<int> Q[2];
void bfs(int x,int y,int tmp){
memset(vis,false,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
dis[x][y]=0; vis[x][y]=1;
Q[0].push(x); Q[1].push(y);
int i,j,ii,jj,t;
while(Q[0].size()){
i=Q[0].front(); Q[0].pop();
j=Q[1].front(); Q[1].pop();
vis[i][j]=0;
for(int k=0;k<4;k++){
ii=i+dx[k]; jj=j+dy[k];
if(ii<1 || ii>n || jj<1 || jj>m) continue;
if((dis[i][j]&1)==tmp){
if(h[ii][jj]<h[i][j]) t=1;
else t=0;
}
else {
if(h[ii][jj]>h[i][j]) t=1;
else t=0;
}
if(dis[i][j]+t<dis[ii][jj]){
dis[ii][jj]=dis[i][j]+t;
if(!vis[ii][jj]) Q[0].push(ii),Q[1].push(jj),vis[ii][jj]=1;
}
}
}
}
bool dfs(int x,int lim){
for(int i=1;i<=tot;i++){
if(val[x][i]>lim || use[i]) continue;
use[i]=1;
if(!match[i] || dfs(match[i],lim)) {
match[i]=x; return 1;
}
}
return 0;
}
inline bool check(int lim){
int ret=0;
memset(match,0,sizeof(match));
for(int i=1;i<=2*k;i++){
memset(use,0,sizeof(use));
ret+=dfs(i,lim);
}
return ret+lim>=2*k;
}
int main(){
n=rd(),m=rd(),k=rd(),T=rd(); int x,y,z;
for(int i=1;i<=2*k+1;i++) xx[i]=rd(),yy[i]=rd();
for(int i=1;i<=T;i++){
x=rd(),y=rd(),z=rd();
while(z--) tx[++tot]=x,ty[tot]=y;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) h[i][j]=rd();
for(int i=1;i<=2*k;i++){
if(i<=k) bfs(xx[i],yy[i],0);
else bfs(xx[i],yy[i],1);
for(int j=1;j<=tot;j++) val[i][j]=dis[tx[j]][ty[j]];
}
int l=0,r=(k<<1),mid;
while(l<=r){
mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}