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;
}
posted @ 2019-02-25 22:06  Monster_Qi  阅读(249)  评论(0编辑  收藏  举报