题意:
2856 Muddy Fields
1.0 秒 262,144.0 KB 80 分 5级题
在一个n*m的草地上有一些泥坑,现在可以用宽度为1,长度任意的长木板去覆盖泥坑,但是木板不能覆盖草地,问最少需要多少长木板?
输入
第一行为两个整数n和m表示草地尺寸
接下来n行,每行一个长为m的字符串表示这片草地(’.’表示草地,’’表示泥坑)
输出
一个整数表示最少需要多少块木板才能覆盖所有泥坑
数据范围
20% 2 <= n <= 5,2 <= m <= 5
60% 2 <= n <= 10,2 <= m <= 10
80% 2 <= n <= 20,2 <= m <= 20
100% 2 <= n <= 50,2 <= m <= 50
输入样例
样例输入1:
4 4
..
.**
.
….
样例输入2:
6 10
*…….
.…**..
.……
….
…….
…..
样例输入3:
6 6
...*
….
….
.*
.**.
*…
输出样例
样例输出1:
4
样例输出2:
16
样例输出3:
9
思路:
经典的二分图匹配
我的想法:
(1)每个泥坑可以被横着的木板覆盖,也可以被竖着的木板覆盖,那么我们就可以在横着的木板编号为x和竖着的木板编号为y之间距建一条边。
(2)我们要求的就是最小顶点覆盖数,什么是最小顶点覆盖?最小顶点覆盖就是G中的任意边都至少一个端点属于S的顶点集合
(3)在二分图中,最大匹配 = 最小顶点覆盖,所以最大流的dinic算法或者二分图的匈牙利算法都可以进行求解
注意:如果名花无主或者或者可以有其他的选择,都可以返回true
代码实现:
//在二分图中最大匹配 = 最小顶点覆盖
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
char s[100][100];
int a[100][100];
int b[100][100];
int n,m;
int G[3000][3000];
int cnt = 0;
int num = 0;
int used[3000];
int match[3000];
bool dfs(int x){
for(int i = 1;i <= num;i++){
if(!used[i]&&G[x][i]){
used[i] = 1;
if(match[i] == -1||dfs(match[i])){
match[i] = x;
return true;
}
}
}
return false;
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++){
scanf(" %s",s[i] + 1);
}
cnt = 0;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(s[i][j] =='*'){
if(s[i][j - 1] == '*') a[i][j] = a[i][j - 1];
else a[i][j] = ++cnt;
}
}
}
num = 0;
for(int j = 1;j <= m;j++){
for(int i = 1;i <= n;i++){
if(s[i][j] == '*'){
if(s[i - 1][j] == '*') b[i][j] = b[i - 1][j];
else b[i][j] = ++num;
}
}
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(s[i][j] == '*'){
G[a[i][j]][b[i][j]] = 1;
//cout << i << ' ' << j << ' ' << a[i][j] << ' ' << b[i][j] << endl;
}
}
}
memset(match,-1,sizeof(match));
int ans = 0;
for(int i = 1;i <= cnt;i++){
memset(used,0,sizeof(used));
if(dfs(i)) ans++;
}
printf("%d\n",ans);
return 0;
}