题目链接:http://poj.org/problem?id=3020
题意是有一个n*m的地图,图中'*'表示城市,现在要给每个城市覆盖无线,需要安装基站,每个基站最多只能覆盖相邻的两个城市,也就是1*2或者2*1的大小,问最少需要安装多少个基站。
正解就是去求无向图的最小边覆盖,因为我们可以把每个城市记一个编号,然后将相邻的两个城市进行建边,这个一条边就相当于一个基站,然后去求最少要用多少条边去覆盖城市,所以就是一个最小边覆盖的问题了,而无向图的最小边覆盖=节点数-最大匹配数/2。这道题的难点其实是怎么去建图,前两天刚写了一道类似的题,而那道题是求最大匹配数的,但是存图方法跟这道题一模一样,想看的可以看看:HDU 4185 Oil Skimming(思维+二分图最大匹配数)。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#define maxn 55
using namespace std;
struct Node{
int to,next;
}Edge[maxn * maxn];
int head[maxn * maxn], num;
string str[maxn];
int a[maxn][maxn];
int pre[maxn * maxn];
bool vis[maxn * maxn];
int dir[4][2] = {1,0,0,1,-1,0,0,-1};
int T,n,m,cnt;
void init(){
memset(head,-1,sizeof(head));
memset(a,0,sizeof(a));
num = 0;
}
void add(int u,int v){
Edge[num].to = v;
Edge[num].next = head[u];
head[u] = num ++;
}
bool OK(int x,int y){
if(x < 0 || y < 0 || x >= n || y >= m)return false;
if(str[x][y] != '*') return false;
return true;
}
bool dfs(int u){
for(int i=head[u];i!=-1;i=Edge[i].next){
int v = Edge[i].to;
if(vis[v] == false){
vis[v] = true;
if(pre[v] == -1 || dfs(pre[v])){
pre[v] = u;
return true;
}
}
}
return false;
}
int solve(){
int sum = 0;
memset(pre,-1,sizeof(pre));
for(int i=1;i<=cnt;i++){
memset(vis,false,sizeof(vis));
if(dfs(i)) sum ++;
}
return sum;
}
int main()
{
scanf("%d",&T);
while(T--){
init();
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
cin>>str[i];
}
cnt = 1;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(str[i][j] == '*'){
a[i][j] = cnt ++;
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(str[i][j] == '*'){
for(int k=0;k<4;k++){
int xx = i + dir[k][0];
int yy = j + dir[k][1];
if(OK(xx, yy)){
add(a[xx][yy], a[i][j]);
add(a[i][j], a[xx][yy]);
}
}
}
}
}
int ans = solve();
printf("%d\n", cnt - ans / 2 - 1);
}
return 0;
}