POJ 2446 Chessboard(二分图匹配)

题目大意:

首先输入m,n,k,表示一个m*n的格子,然后又k组数据,每组数据包含l,c,表示在这个m*n的格子中第l列第c行的小方格是空的,现在有若干1*2的卡片,要求用这些卡片覆盖掉所有的非空的方格,且卡片不能重叠。如果能够完全覆盖,则输出YES,否则输出NO.

解题思路:

对我来说是很难想到用二分图匹配的方法来做的。后来看了网上的题解,才明白用二分图匹配是很简单的,只是在建图的时候有些困难。现在来分析一下方格的特点:每个方格都是和上下左右的一个方格共同被一个卡片覆盖。如果一个格子的行号加列号为偶数,那么与它相邻的格子的行号加列号必定是奇数。如果一个格子的行号加列号为奇数,那么与它相邻的格子的行号加列号必定是偶数。现在把行号加列号为偶数的集合加入二分图的X集合,把行号加列号为奇数的集合加入到二分图的Y集合,现在对于一个非空格子,只需把与这个格子相邻的左边和上边的格子建立关联就可以了(想一想,为什么)。好了,现在就可以进行二分图匹配了。如果最大二分图匹配数*2=非空格子数,则输出YES,否则输出NO.

题目给的例子:

0表示格子非空,1表示格子是空的。括号内是行号加列号的值。

0(0)10(2)
0(1)0(2)0(3)
0(2)0(3)1
0(3)0(4)0(5)

现在将行号加列号为偶数的格子进行从0~uN-1的编号:

0 1
 2 
3  
 4 

现在将行号加列号为奇数的格子进行从0~vN-1的编号:

   
0 1
 2 
3 4

然后就可以求X集合和Y集合的最大匹配:

代码:

#include<iostream>
#include<fstream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<sstream>
#include<cassert>
using namespace std;
#define LL __int64
int m,n,k;

const int MAXN = 2000;
int uN, vN; // u, v数目,要初始化!!!
bool g[MAXN][MAXN]; // g[i][j] 表示xi与yj相连
int xM[MAXN], yM[MAXN]; // 输出量
bool chk[MAXN]; // 辅助量检查某轮y[v]是否被check
bool SearchPath(int u)
{
    int v;
    for(v = 0; v < vN; v++)
        if(g[u][v] && !chk[v])
        {
            chk[v] = true;
            if(yM[v] == -1 || SearchPath(yM[v]))
            {
                yM[v] = u;
                xM[u] = v;
                return true ;
            }
        }
    return false ;
}
int MaxMatch()
{
    int u, ret = 0 ;
    memset(xM, -1, sizeof (xM));
    memset(yM, -1, sizeof (yM));
    for(u = 0; u < uN; u++)
        if(xM[u] == -1)
        {
            memset(chk, false, sizeof (chk));
            if(SearchPath(u)) ret++;
        }
    return ret;
}

int mark[MAXN][MAXN];
int th[MAXN][MAXN];
int main()
{
    while(~scanf("%d%d%d",&m,&n,&k))
    {
        memset(g,false,sizeof(g));
        memset(mark,0,sizeof(mark));
        memset(th,0,sizeof(th));
        uN=vN=0;
        int c,l;
        for(int i=0;i<k;i++){
            scanf("%d%d",&l,&c);
            mark[c-1][l-1]=1;        //标记空的格子
        }
        for(int i=0; i<m; i++){      //对格子进行编号,分别加入到X集合和Y集合
            for(int j=0; j<n; j++){
                if(mark[i][j]==0){
                    if((i+j)%2==0){
                        th[i][j]=uN;
                        uN++;
                    }
                    else{
                        th[i][j]=vN;
                        vN++;
                    }
                }
            }
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(mark[i][j]==0){
                    int uth,vth;
                    uth=i*n+j;
                    if((i+j)%2==0){   //行号加列号是偶数
                        if((i-1)>=0 && mark[i-1][j]==0){
                            g[th[i][j]][th[i-1][j]]=true; //与上边相邻的格子建立关系
                        }
                        if(j-1>=0 && mark[i][j-1]==0){    //与左边相邻的格子建立关系
                            g[th[i][j]][th[i][j-1]]=true;
                        }
                    }
                    else{           //行号加列号是奇数
                        if((i-1)>=0 && mark[i-1][j]==0){
                            vth=(i-1)*n+j;
                            g[th[i-1][j]][th[i][j]]=true;
                        }
                        if(j-1>=0 && mark[i][j-1]==0){
                            vth=i*n+j-1;
                            g[th[i][j-1]][th[i][j]]=true;
                        }
                    }
                }
            }
        }
        if((vN+uN)%2){
            printf("NO\n");
            continue;
        }
        int ans=MaxMatch();
        if(ans*2==(vN+uN)) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值