UVA-11419 SAM I AM (最小点覆盖)

题目大意:在一个n*m的网格中,有k个目标,现在可以任选一行或列消除在其上的所有目标,求出最少选择次数及选法。

题目分析:经典的最小点覆盖问题,并且输出一个最小点覆盖集。在求出最大匹配之后,以未覆盖的x点进行标记,沿着未覆盖->覆盖->未覆盖->覆盖...的路径标记,最后x中未标记的和y中标记的点构成最小点覆盖集。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
# define REP(i,s,n) for(int i=s;i<n;++i)
# define CL(a,b) memset(a,b,sizeof(a))
# define CLL(a,b,n) fill(a,a+n,b)

const int N=1005;
struct Edge
{
    int to,nxt;
};
Edge e[N*N];
int link[N],visx[N],visy[N],vis[2*N],mark[N];
int cnt,n,m,head[N];

void add(int u,int v)
{
    e[cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt++;
}

void dfs1(int x)
{
    visx[x]=1;
    for(int i=head[x];i!=-1;i=e[i].nxt){
        int y=e[i].to;
        if(visy[y]) continue;
        visy[y]=1;
        dfs1(link[y]);
    }
}

bool dfs(int x)
{
    for(int i=head[x];i!=-1;i=e[i].nxt){
        int y=e[i].to;
        if(vis[y]) continue;
        vis[y]=1;
        if(link[y]==-1||dfs(link[y])){
            link[y]=x;
            return true;
        }
    }
    return false;
}

int match()
{
    int res=0;
    REP(i,1,n+1){
        CL(vis,0);
        if(dfs(i)) ++res;
    }
    return res;
}

int main()
{
    int a,b,k;
    while(scanf("%d%d%d",&n,&m,&k)&&(n+m+k))
    {
        cnt=0;
        CL(head,-1);
        CL(link,-1);
        CL(mark,0);
        CL(visx,0);
        CL(visy,0);
        while(k--)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
        }
        int ans=match();
        REP(i,1,m+1) if(link[i]!=-1) mark[link[i]]=1;
        REP(i,1,n+1) if(!mark[i]) dfs1(i);
        printf("%d",ans);
        REP(i,1,n+1) if(!visx[i]) printf(" r%d",i);
        REP(i,1,m+1) if(visy[i]) printf(" c%d",i);
        printf("\n");
    }
    return 0;
}

  

posted @ 2015-11-05 22:24  20143605  阅读(418)  评论(0编辑  收藏  举报