POJ3041 Asteroids【二分图最小点覆盖】

题目链接:

http://poj.org/problem?id=3041


题目大意:

有一个N*N的矩阵,有些格子上有障碍物(坐标为(x,y) ),在消除这些障碍物的时候,可以一次性消除

该障碍物同一行所有的障碍物,或是一次性消除该障碍物同一列所有的障碍物。只能选择清理该行或是

清理该列。问:最小进行多少次消除,就可以清理所有的障碍物。


思路:

可以将每一行当做一个点,这样总共有N个点,作为二分图的一边。将每一列当做一个点,这样又有N

个点,作为二分图的另一边。将有障碍物的行点和列点连接起来,每次只能选择行或是列,需要清除所

有的障碍物。所以不能存在一条边两边的点都没有被选中的情况。这样问题就转变为选择最少的一些点

(行点或是列点),使得从这些点与所有的边相连,也就是二分图最小点覆盖问题。


AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 550;

bool Map[MAXN][MAXN],Mask[MAXN];

int NX,NY;
int cx[MAXN],cy[MAXN];

int FindPath(int u)
{
    for(int i = 1; i <= NY; ++i)
    {
        if(Map[u][i] && !Mask[i])
        {
            Mask[i] = 1;
            if(cy[i] == -1 || FindPath(cy[i]))
            {
                cy[i] = u;
                cx[u] = i;
                return 1;
            }
        }
    }
    return 0;
}

int MaxMatch()
{
    for(int i = 1; i <= NX; ++i)
        cx[i] = -1;
    for(int i = 1; i <= NY; ++i)
        cy[i] = -1;

    int res = 0;
    for(int i = 1; i <= NX; ++i)
    {
        if(cx[i] == -1)
        {
            for(int j = 1; j <= NY; ++j)
                Mask[j] = 0;
            res += FindPath(i);
        }
    }
    return res;
}

int main()
{
    int N,M,u,v;
    while(~scanf("%d%d",&N,&M))
    {
        NX = NY = N;
        memset(Map,0,sizeof(Map));
        for(int i = 1; i <= M; ++i)
        {
            scanf("%d%d",&u,&v);
            Map[u][v] = 1;
        }
        printf("%d\n",MaxMatch());
    }

    return 0;
}


你可能感兴趣的:(POJ3041 Asteroids【二分图最小点覆盖】)