在一块 N×M 的网格状地面上,有一些格子是泥泞的,其他格子是干净的。
现在需要用一些宽度为 1、长度任意的木板把泥地盖住,同时不能盖住干净的地面。
每块木板必须覆盖若干个完整的格子,木板可以重叠。
求最少需要多少木板。
输入格式
第一行包含两个整数 N 和 M。
接下来 N 行,每行 M 个字符,用来描述地面,*
表示泥泞格子,.
表示干净格子,字符之间没有空格。
输出格式
输出一个整数,表示结果。
数据范围
1≤N,M≤50
输入样例:
4 4
*.*.
.***
***.
..*.
输出样例:
4
分析:图论+二分图最小点覆盖
对于每块泥块都需要被横向或竖向的木板至少覆盖一次。由于木板不能盖住干净的地面,所以横着的木板只能覆盖同一行若干个连续的泥块。竖着的木板只能覆盖同一列若干个连续的泥块。我们把这些连续的泥块分别分成 行泥块 和 列泥块。对于每个泥块,把它所在的行泥块和列泥块之间连一条边。把行泥块当作左部节点,列泥块当作右部节点。我们只需要保证每条边至少有一个节点被覆盖即可。因此求的就是二分图的最小点覆盖,等价于求最大匹配数,用匈牙利算法来解决。
代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N=55;
bool st[N*N],g[N*N][N*N];
int row[N][N],col[N][N];//行泥块与列泥块所属的id
int match[N*N];
char a[N][N];
int n,m,ans,rcnt,ccnt;
bool dfs(int id)
{
for(int i=1;i<=ccnt;i++)
{
if(!g[id][i]||st[i])continue;
st[i]=true;
if(!match[i]||dfs(match[i]))
{
match[i]=id;
return true;
}
}
return false;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)cin>>a[i][j];
for(int i=1;i<=n;i++)//行
{
for(int j=1;j<=m;j++)
{
if(a[i][j]=='.')continue;
rcnt++;
while(j<=m&&a[i][j]=='*')
{
row[i][j]=rcnt;
j++;
}
j--;
}
}
for(int i=1;i<=m;i++)//列
{
for(int j=1;j<=n;j++)
{
if(a[j][i]=='.')continue;
ccnt++;
while(j<=n&&a[j][i]=='*')
{
col[j][i]=ccnt;
j++;
}
j--;
}
}
for(int i=1;i<=n;i++)//行
{
for(int j=1;j<=m;j++)//列
{
if(a[i][j]=='.'||g[row[i][j]][col[i][j]])continue;
g[row[i][j]][col[i][j]]=true;
}
}
for(int i=1;i<=rcnt;i++)//行泥块编号
{
memset(st,0,sizeof st);
if(dfs(i)) ans++;
}
cout<<ans;
return 0;
}