题目大意:
首先输入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) | 1 | 0(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;
}