这题和poj3041有点类似,但是建图方式却不是一样的,因为这题要求放的板子不能覆盖了草。
样例是这样的,下面就讲讲怎么建图
4 4
*.*.
.***
***.
..*.
把行里面连在一起的坑连起来视为一个点,即一块横木板,编上序号,Sample则转化为:
1 0 2 0
0 3 3 3
4 4 4 0
0 0 5 0
需要的板子数为u
再一列一列的操作一次,需要的板子数是v
1 0 4 0
0 3 4 5
2 3 4 0
0 0 4 0
一个坑只能被横着的或者被竖着的木板盖住,将原图的坑的也标上不同的序号,一共九个坑
1 . 2 .
. 3 4 5
6 7 8 .
. . 9 .
预处理完之后,所有横向板子即第一个点集,所有竖向板子就是第二个点集,然后再确定边关系,也就是哪些点之间有边,该边就代表了一个洼点,遍历这个图,遇到一个‘*',就说明,其对应的横向板子编号和其对应的竖向板子编号之间存在一条边,然后将对应的点连起来。建完图就二分图最大匹配就行了。可以把这个二分图里的点理解为是板子,然后边理解为是洼地,要求用最少的板子,也就是求最少用最少的点来覆盖所有边。
最后总结一些常用的定理
(1)二分图的最小顶点覆盖
最小顶点覆盖要求用最少的点(X或Y中都行),让每条边都至少和其中一个点关联。
Knoig定理:二分图的最小顶点覆盖数等于二分图的最大匹配数。
(2)DAG图的最小路径覆盖
用尽量少的不相交简单路径覆盖有向无环图(DAG)G的所有顶点,这就是DAG图的最小路径覆盖问题。
结论:DAG图的最小路径覆盖数 = 节点数(n)- 最大匹配数(m)
(3)二分图的最大独立集
最大独立集问题: 在N个点的图G中选出m个点,使这m个点两两之间没有边.求m最大值
结论:二分图的最大独立集数 = 节点数(n)— 最大匹配数(m)
#include<stdio.h>
#include<iostream>
#include<vector>
#include<string.h>
using namespace std;
int used[2505],r[55][55],c[55][55],nxt[2505];
vector<int>g[2500];
char mp[55][55];
int h,l,u,v;
bool ifind(int x)
{
int sz=g[x].size();
for(int i=0;i<sz;i++)
{
int p=g[x][i];
if(!used[p])
{
used[p]=1;
if(nxt[p]==0||ifind(nxt[p]))
{
nxt[p]=x;
return true;
}
}
}
return false;
}
int match()
{
int ans=0;
for(int i=1;i<=u;i++)
{
memset(used,0,sizeof(used));
if(ifind(i))
ans++;
}
return ans;
}
int main()
{
std::ios::sync_with_stdio(false);
cin>>h>>l;
u=0;
v=0;
for(int i=0;i<h;i++)
cin>>mp[i];
for(int i=0;i<h;i++)
for(int j=0;j<l;j++)
{
if(mp[i][j]=='*')
{
u++;
while(j<l&&mp[i][j]=='*')
{
r[i][j]=u;
j++;
}
}
}
for(int j=0;j<l;j++)
for(int i=0;i<h;i++)
{
if(mp[i][j]=='*')
{
v++;
while(i<h&&mp[i][j]=='*')
{
c[i][j]=v;
i++;
}
}
}
for(int i=0;i<h;i++)
for(int j=0;j<l;j++)
if(mp[i][j]=='*')
{
g[r[i][j]].push_back(c[i][j]);
}
/*cout<<"&************88"<<endl;
for(int i=1;i<=u;i++)
{
int sz=g[i].size();
cout<<i<<":";
for(int j=0;j<sz;j++)
cout<<g[i][j]<<" ";
cout<<endl;
}
cout<<"***********8"<<endl;
*/
cout<<match()<<endl;
return 0;
}