P1524柯南开锁
时间限制 : 10000 MS 空间限制 : 65536 KB
问题描述
柯南决定深入OIBH组织内部, 一探虚实.他经过深思熟虑, 决定从OIBH组织大门进入...........
OIBH组织的大门有一个很神奇的锁.锁是由M*N个格子组成, 其中某些格子凸起(灰色的格子). 每一次操作可以把某一行或某一列的格子给按下去.
如果柯南能在组织限定的次数内将所有格子都按下去, 那么他就能够进入总部. 但是OIBH组织不是吃素的, 他们的限定次数恰是最少次数
请您帮助柯南计算出开给定的锁所需的最少次数.
输入格式
第一行 两个不超过100的正整数N, M表示矩阵的长和宽
以下N行 每行M个数 非0即1 1为凸起方格
输出格式
一个整数 所需最少次数
样例输入
4 4
0000
0101
0000
0100
样例输出
2
你明明应该很虚,然而你却选择从大门堂而皇之地撬锁进去
题解
约束条件
当你按下某一个点(x,y)时,你可以选择按行,也可以选择按列
但是,当你按下这一行时,这一列上就少了一个点
同理,按下这一列时,这一行上也少了一个点
例如
0 1 1 0
0 1 0 0
0 0 0 0
我们按下第1行第2列的行时,第一行全部都变为了0,同时第二列也少了一个需要按下去的点
那么约束条件就是在x行和y列上连边
当你连上某条边时,代表的就是那一行或那一列全部的点全部被按了
那么x点连接的其他边或y点连接的其他边就无需再连了
然后求最大匹配即可
解疑
为什么按下一个点(x,y)这一行(x)和这一列(y)都不需要再按了呢??
说的意会一点就是
这就是求最小点覆盖集嘛
仔细解释一下
当我们求到了最小点覆盖集的时候
有点的每一行/每一列都被覆盖了
那么我们可以理解为把每一行/每一列都按下去,这一定是一个解
即:即使我们不选择最优解,把有点的每一行都按下去都一定是一个解
那么,由于我们求的是最小点覆盖,所以这个得到的就一定是最优解
换句话说,我们求到的题目的解也必须要满足覆盖有点的每一行/每一列,才是一个正常解
附上代码
#include <iostream>
#include <cstdio>
using namespace std;
int n,m,res=0;
char maps[123][123];
int all=0,star[234],nxt[12345],ent[12345];
int fath[234],went[234];
void add(int s,int e)
{
nxt[++all]=star[s];
star[s]=all;
ent[all]=e;
}
bool find (int s,int t)//求最大匹配
{
int bian,e;
for(bian=star[s],e=ent[bian];bian;bian=nxt[bian],e=ent[bian])
if(went[e]!=t)
{
went[e]=t;
if(!fath[e]||find(fath[e],t))
{
fath[e]=s;
return 1;
}
}
return 0;
}
int main()
{
scanf("%d%d\n",&n,&m);
for(int a=1;a<=n;a++)scanf("%s",&maps[a]);
for(int a=1;a<=n;a++)
for(int b=0;b<m;b++)
if(maps[a][b]=='1')
add(a,n+b+1);//构二分图
for(int i=1;i<=n+m;i++)res+=find(i,i);
printf("%d",res);
}