把每一列当成一个点,每一行当成一个点,若行节点和列节点之间有边,则表明该行列该列有一个障碍物。
可以放炸弹炸掉成行或者成列的障碍物!
主要是构图:将每一行当成一个点,构成集合1, 每一列也当成一个点,构成集合2;每一个障碍物的位置坐标将集合1与集合2中的点连接起来,也就是将每一个障碍物作为连接节点的边。这样可以轻易的得出本题是一个最小点覆盖的问题,假设1个行节点覆盖了5个列节点,即这个行节点与这5个列节点间有5条边(即五个障碍物),由于这5条边都被那个行节点覆盖,即表明这5个障碍物都在同一列上,于是可以一颗炸弹全部清除,而本题也就转化成求最小点覆盖数的问题。
又有一个定理是:最小点覆盖数 = 最大匹配数, 所以此题转化成求最大匹配数。
最大匹配算法又称匈牙利算法
//思路:将行看做U集合,列看做V集合,进行增广路的寻找
#include<stdio.h>
#include<string.h>
#define max 10002
int map[502][502];
bool visit[max];
int match[max];
int n;
int path(int start)
{
int i,j;
for(i=1;i<=n;i++) //找从U的某个点与V集合的有关系的点
{
if(visit[i]==false && map[start][i]==1)
{
visit[i]=1; //如果以U的第一个点开始的V的点跟start有关系的话,那么记录
if(match[i]==-1 || path(match[i])) //如果找到的点已经找到匹配的话,那么继续找
{
match[i]=start;
return true;
}
}
}
return false;
}
int main()
{
int m;
scanf("%d%d",&n,&m);
memset(map,0,sizeof(map));
memset(match,-1,sizeof(match));
int i,j;
int a,b;
for(i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
map[a][b]=1; //有向图
}
int result=0;
for(i=1;i<=n;i++) //从U集合的第一个数字寻找
{
memset(visit,false,sizeof(visit));
if(path(i))
result++;
}
printf("%d\n",result);
return 0;
}