poj 2446(二分图匹配-通俗易懂)

题目链接:http://poj.org/problem?id=2446

题目大意:给出一个N*M的棋盘,棋盘有些格子是不能放东西的。现在要判断在所以可以放东西的格子上是否能用1*2的小方块填满

题目思路:

小木块有两个格子,对于棋盘上的每一个格子,如果可以放东西,那么小木块的另一个格子一定在棋盘上这个格子的上下左右。

那么对于整个棋盘,从上到下从左到右给它编号1到n*m,并将每一个格子(如果可以放东西)与其上下左右可以放东西的格子相连~就形成了一个二分图。最后只要最大独立集等于所有顶点数减去最大匹配数,说明就可以放满

#include<iostream>
#include<cstring>
#include<cstdio>
#define me(k) memset(k,0,sizeof(k))
using namespace std;
int dir[4][2]={1,0,-1,0,0,1,0,-1};
int a[35][35],m,n;
int map[1100][1100];//用来建立二分用过图
int linker[1100];
bool use[1100];//表示有没有
void build(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        int x1=x+dir[i][0];
        int y1=y+dir[i][1];
        if(x1>=1&&x1<=m&&y1>=1&&y1<=n&&a[x1][y1]==0)
        {
            map[(x-1)*n+y][(x1-1)*n+y1]=1;//表示有一条连线
        }
    }
}
bool dfs(int u)
{
    for(int i=1;i<=m*n;i++)
        if(map[u][i]&&!use[i])
    {
        use[i]=true;
        if(linker[i]==-1||dfs(linker[i]))
        {
            linker[i]=u;
            return true;
        }
    }
    return false;
}
int hungary()
{
    memset(linker,-1,sizeof(linker));
    int ans=0;
    for(int i=1;i<=n*m;i++)
    {
        memset(use,false,sizeof(use));
        if(dfs(i)) ans++;
             }
             return ans;
}
int main()
{
    int k;
    while(~scanf("%d%d%d",&m,&n,&k))
    {
        me(a);
        me(map);
        int r,c;
        for(int i=1;i<=k;i++)
        {
            scanf("%d%d",&r,&c);
            a[c][r]=1;//表示为坑
        }
        for(int i=1;i<=m*n;i++)
        {
            int x=i/n+1;
            int y=i%n;
            if(y==0) {x--;y=n;}
            if(a[x][y]==0)
            {
                build(x,y);//去建立二分图
            }
        }
        /*for(int i=1;i<=m*n;i++)
        {

        for(int j=1;j<=m*n;j++)
            cout<<map[i][j]<<" ";
        cout<<endl;

        }*/
        int ans=hungary();
       // printf("%d\n",ans);
        if(ans==n*m-k) printf("YES\n");
        else printf("NO\n");

    }



    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值