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;
}
}