原题:http://poj.org/problem?id=3020
题意:
给出一个n*m的矩阵, ‘ o ’ 表示空地,' * ' 表示城市;
现要这些城市都覆盖无线网,在一些城市建基站,一个基站可以至多覆盖相邻的两个城市(这里的两个城市指建了基站的城市和相邻的一个城市);
问最少需要建立几个基站;
思路:
可以想到是最小边覆盖;
此处构建二分图并不是简单的把坐标分为X集和Y集;
我们可以按顺序对城市进行标号,空地就记为0;
例:**o
o*o
***
我们可以记为:120
030
456
所以X、Y集即为城市标号;同时,我们可以看出,如果基站建在城市1,那么可以覆盖2,如果基站建在城市2,也可以覆盖1,可见该二分图是无向的;
接下来就是求一个最大匹配数了;
无向二分图的最小边覆盖 = 顶点数-最大匹配数/2;
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 50*50;
int n, m, cas;
char str[maxn];
int map[maxn][maxn];
bool used[maxn];
int match[maxn];
int f[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};
vector<int>G[maxn];
bool find(int x)
{
for(int i = 0;i<G[x].size();i++)
{
int k = G[x][i];
if(!used[k])
{
used[k] = true;
if(match[k] == 0 || find(match[k]))
{
match[k] = x;
return true;
}
}
}
return false;
}
int main()
{
scanf("%d", &cas);
while(cas--)
{
memset(map, 0, sizeof map);
memset(match, 0, sizeof match);
scanf("%d%d", &n, &m);
int cnt = 0;
for(int i = 1;i<=n;i++)
{
scanf("%s", str);
for(int j = 0;j<strlen(str);j++)
{
if(str[j] == '*')
map[i][j] = ++cnt;
}
}
for(int i = 1;i<=cnt;i++)
G[i].clear();
for(int i = 1;i<=n;i++)
{
for(int j = 0;j<m;j++)
{
if(map[i][j] == 0) continue;
for(int t = 0;t<4;t++)
{
int a = i+f[t][0];
int b = j+f[t][1];
if(a<1 || a>n || b<0 || b>m-1 || !map[a][b]) continue;
G[map[i][j]].push_back(map[a][b]);
}
}
}
int sum = 0;
for(int i = 1;i<=cnt;i++)
{
memset(used, false, sizeof used);
if(find(i))
sum++;
}
printf("%d\n", cnt-sum/2);
}
return 0;
}