题目链接 http://poj.org/problem?id=2226
题目大意:
在一块 N*M 的网格状地面上,有一些格子是泥泞的,其他格子是干净的。
现在需要用一些宽度为1、长度任意的木板把泥地盖住,同时不能盖住干净的地面。
每块木板必须覆盖若干个完整的格子,木板可以重叠。
求最少需要多少木板。
输入格式
第一行包含两个整数 N 和 M。
接下来N行,每行M个字符,用来描述地面,‘*’表示泥泞格子,‘.’表示干净格子,字符之间没有空格。
输出格式
输出一个整数,表示结果。数据范围1≤N,M≤50
思路 :
先说说最小覆盖,就是对于二分图中的边,使得边的两个端点至少一个存在于点集S,最小的点集S就是最小覆盖。可以换一个更朴素的说法,就是对于边的两个端点而言,可以把其中过一个放到点集中或者把另一个放入点集中,即是可选择的。这也是最下覆盖的一个重要的标志,就是二者至少满足其一即可。
本题和最小覆盖的联系不是很直观。对于一个点,如果是泥泞,肯定需要木板来覆盖,这个木板可能横着放,也可能竖着放,但是只要放了就可以覆盖此点,那么对于这个点而言,就是横着放一块板或者竖着放一块板二者至少选其一,这就是本题和最小覆盖的联系了。如果我们把横着连续泥泞和竖着连续泥泞的叫做行连通块和列连通块。那么显然,每一个泥泞点既属于一个行连通块又属于一个列连通块,映射到图中就是边的两个端点。所以可以先求出所有的行列连通块,然后建图,将所有泥泞的点的行连通块和列连通块连成一条边,求最小覆盖即可。
//============================================================================
// Name : test.cpp
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int N = 3e4;
int head[N], nex[N], to[N], cnt;
inline void add(int a, int b){
++cnt;
to[cnt] = b;
nex[cnt] = head[a];
head[a] = cnt;
}
int n, m;
char s[55][55];
int row[55][55], column[55][55];
int rows, columns;
int match[N];
bool vis[N];
void pre_work(){
cnt = 0;
mem(head, -1);
mem(nex, -1);
mem(match, -1);
rows = 1;
columns = 1;
for (int i = 1; i <= n; i++){
int j = 1;
while (j <= m){
while (j <= m && s[i][j] == '*'){
row[i][j] = rows;
j++;
}
++rows;
while (j <= m && s[i][j] != '*'){
j++;
}
}
}
for (int i = 1; i <= m; i++){
int j = 1;
while (j <= n){
while (j <= n && s[j][i] == '*'){
column[j][i] = columns;
++j;
}
++columns;
while (j <= n && s[j][i] != '*'){
++j;
}
}
}
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
if (s[i][j] == '*'){
add(row[i][j], column[i][j] + rows + 10);
add(column[i][j] + rows + 10, row[i][j]);
}
}
}
}
bool dfs(int x){
for (int i = head[x]; i != -1; i = nex[i]){
int y = to[i];
if (vis[y])continue;
vis[y] = 1;
if (match[y] == -1 || dfs(match[y])){
match[y] = x;
return 1;
}
}
return 0;
}
int main()
{
scanf("%d %d", &n, &m);
getchar();
for (int i = 1; i <= n; i++){
scanf("%s", s[i] + 1);
}
pre_work();
int ans = 0;
for (int i = 1; i < rows; i++){
mem(vis, 0);
if (dfs(i))++ans;
}
printf("%d\n", ans);
return 0;
}