UVa:11419 SAM I AM

行为左边点集,列为右边点集,障碍为边,这样消灭所有障碍即每条边都连着一个点。这样选最少的点即可。这样问题就是求解最小点覆盖集,在二分图中等价于二分最大匹配。但是这里还要求出最小点覆盖集。方法是,在求出二分图最大匹配后,寻找左边点中的未盖点进行拓展匈牙利树,将所有遍历到的点标记。这样左边中为标记的,右边的标记的点即为最小点覆盖集。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#define ll long long
#define INF 1e30
#define inf -2139062144
#define MOD 20071027
#define MAXN 1005
using namespace std;
bool gl[MAXN][MAXN];
bool visy[MAXN],visx[MAXN];
int link[MAXN];
int R,C,N;
int flagx[MAXN],flagy[MAXN];
bool match(int x)
{
    flagx[x]=true;
    for(int i=1; i<=C; ++i)
        if(!visy[i]&&gl[x][i])
        {
            visy[i]=true;
            flagy[i]=true;
            if(link[i]==-1||match(link[i]))
            {
                link[i]=x;
                return true;
            }
        }
    return false;
}

int main()
{
    while(scanf("%d%d%d",&R,&C,&N))
    {
        if(!R&&!C&&!N) break;
        memset(link,-1,sizeof(link));
        memset(gl,0,sizeof(gl));
        memset(visx,0,sizeof(visx));
        for(int i=0; i<N; ++i)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            gl[x][y]=true;
        }
        int ans=0;
        for(int i=1; i<=R; ++i)
        {
            memset(visy,0,sizeof(visy));
            if(match(i)) ans++;
        }
        memset(flagx,0,sizeof(flagx));
        memset(flagy,0,sizeof(flagy));
        printf("%d",ans);
        for(int i=1; i<=C; ++i)
            visx[link[i]]=true;
        for(int i=1; i<=R; ++i)
        {
            memset(visy,0,sizeof(visy));
            if(!visx[i]) match(i);
        }
        for(int i=1; i<=R; ++i)
            if(!flagx[i]) printf(" r%d",i);
        for(int i=1; i<=C; ++i)
            if(flagy[i]) printf(" c%d",i);
        printf("\n");
    }
    return 0;
}


 

你可能感兴趣的:(二分图,二分图最大匹配,匈牙利算法,最小点覆盖集)