POJ2226Muddy Fields

本文介绍了解决POJ 2226问题的方法,通过将行或列上的联通块视为图的节点,并使用泥格子作为边,最终转化为求最小点覆盖问题。利用匈牙利算法寻找最大匹配,特别注意记录每个右部节点对应的左部节点的状态。

题目:http://poj.org/problem?id=2226

巧妙建图:以行或列上的联通块作为点,每个泥格子作为边,求最小点覆盖就可以了!

于是用匈牙利算法找最大匹配。注意要对右部点记录每一个左部点的vis!

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int n=55,N=((n*n)<<1);
int r,c,head[N],xnt=1,stack[n],top,cnt,col[n][n],pre[N],knt;
char ch[n][n];
bool vis[N];
struct Edge{
    int next,to;
    Edge(int n=0,int t=0,int c=0):next(n),to(t) {}
}edge[N];
bool check(int cur)
{
    for(int i=head[cur],v;i;i=edge[i].next)
        if(!vis[v=edge[i].to])
        {
            vis[v]=1;
            if(!pre[v=edge[i].to]||check(pre[v]))
            {pre[v]=cur;/*printf("cur=%d v=%d\n",cur,v);*/return true;}
        }
    return false;
}
void cz1(int i)
{
    cnt++;
    while(ch[i][stack[top]]=='*')col[i][stack[top--]]=cnt;
}
void cz2(int i)
{
    cnt++;
    while(ch[stack[top]][i]=='*')
    {
        edge[++xnt]=Edge(head[col[stack[top]][i]],cnt);
//        printf("%d %d\n",col[stack[top]][i],cnt);
        head[col[stack[top--]][i]]=xnt;
    }
}
int main()
{
    scanf("%d%d",&r,&c);
    for(int i=1;i<=r;i++)
    {
        getchar();
        for(int j=1;j<=c;j++)
        {
            scanf("%c",&ch[i][j]);
            if(ch[i][j]=='*')stack[++top]=j;
            else if(top)cz1(i);
        }
        if(top)cz1(i);
        top=0;
    }
//    for(int i=1;i<=r;i++)
//    {
//        for(int j=1;j<=c;j++)
//            printf("%d",col[i][j]);printf("\n");
//    }
    int lm=cnt;
    for(int i=1;i<=c;i++)
    {
        for(int j=1;j<=r;j++)
        {
            if(ch[j][i]=='*')stack[++top]=j;
            else if(top)cz2(i);
        }
        if(top)cz2(i);
        top=0;
    }
    for(int i=1;i<=lm;i++)
    {
        memset(vis,0,sizeof vis);//万一同一次踩在同一个未匹配右部点的话 
        if(check(i))knt++;    //右边不会主动匹配左边 ,这个i一定未匹配 
    }
    printf("%d",knt);
    return 0;
}

 

转载于:https://www.cnblogs.com/Narh/p/8868489.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值