POJ [P3020] Antenna Placement

二分图匹配求最小边覆盖

建图方法中的黑白染色法,题目中说信号可以覆盖相邻两个块,那么我们可以将给定的地图染成国际象棋棋盘的样子,一个黑格可以与周围的四个白格共用信号,对于城市,从每一个黑格出发,向其周围的白格连边,那么这就是一个二分图,我们的把城市抽象成了点,所以我们的目的是找到最少的边覆盖所有的点,即最小边覆盖。
在二分图中,最小边覆盖=顶点数-最大匹配数。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int init(){
	int rv=0,fh=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') fh=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		rv=(rv<<1)+(rv<<3)+c-'0';
		c=getchar();
	}
	return fh*rv;
}
int T,n,m,g[405][5],rs[50][50],clr[50][50],dx[4]={-1,0,0,1},dy[4]={0,-1,1,0},match[405];
bool f[405];
char ma[50][50];
bool hungarian(int u){
	for(int i=1;i<=g[u][0];i++){
		int v=g[u][i];
		if(!f[v]){
			f[v]=1;
			if(!match[v]||hungarian(match[v])){
				match[v]=u;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	T=init();
	while(T--){
		n=init();m=init();
		memset(ma,0,sizeof(ma));
		memset(rs,0,sizeof(rs));
		memset(clr,0,sizeof(clr));
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(j==1) rs[i][j]=!rs[i-1][j];
				else rs[i][j]=!rs[i][j-1];
				scanf(" %c ",&ma[i][j]);
			}
		}
		int blk=0,wit=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(ma[i][j]=='*'){
					if(rs[i][j]) clr[i][j]=++wit;
					else clr[i][j]=++blk;
				}else clr[i][j]=0;
			}
		}
		memset(g,0,sizeof(g));
		memset(match,0,sizeof(match));
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(clr[i][j]&&rs[i][j]){
					int u=clr[i][j];
					for(int k=0;k<=3;k++){
						int x=i+dx[k],y=j+dy[k];
						if(clr[x][y]) g[u][++g[u][0]]=clr[x][y];
					}
				}
			}
		}
		int ans=0;
		for(int i=1;i<=wit;i++){
			memset(f,0,sizeof(f));
			if(hungarian(i)) ans++;
		}
		cout<<wit+blk-ans<<endl;
		//cout<<ans<<endl;
	}
}
posted @ 2018-01-12 19:48  Mr_Wolfram  阅读(226)  评论(0编辑  收藏  举报