Description
给定一个\(m\;\times\;n\)的棋盘,上面有\(k\)个洞,求是否能在不重复覆盖且不覆盖到洞的情况下,用\(2\;\times\;1\)的卡片完全覆盖棋盘。
Input
第一行有三个整数\(n,m,k(0<m,n\;\leq\;32,0\;\leq\;k<m\;\times\;n),m\)表示行数,\(n\)表示列数。
接下来\(k\)行,每行两个整数\(y,x\),表示\((x,y)\)上有个洞。
Output
如果能覆盖,输出\(YES\);否则输出\(NO\)。
Sample Input
4 3 2
2 1
3 3
Sample Output
YES
Solution
\(32\;\times\;32\)的范围如果暴搜剪枝能力比较强的人也许能过吧,但是我并没有那个能力。
\(2\;\times\;1\)的卡片去覆盖的话,就是两个相邻的格子为一对,且每个格子只能属于一对(有洞的格子不属于任何一对)。
怎么有点像二分图匹配?那就想想怎么构图吧。
定义两格子相邻当且仅当它们有一条公共边时。
那么两个相邻的格子就得在不同集合里,按这样分正好能分成两个集合。
然后两个可放卡片的相邻格子连一条边,这样二分图就构成功了。
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define D 5
#define N 35
#define K 513
#define M 2049
using namespace std;
struct graph{
int nxt,to;
}e[M];
int x[D]={0,0,0,1,-1},y[D]={0,1,-1,0,0};
int g[K],fr[N*N],m,n,k,cnt,tot,sum;
bool b[N][N],u[N*N];
inline void addedge(int x,int y){
e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline bool match(int k){
for(int i=g[k];i;i=e[i].nxt)
if(!u[e[i].to]){
u[e[i].to]=true;
if(!fr[e[i].to]||match(fr[e[i].to])){
fr[e[i].to]=k;return true;
}
}
return false;
}
inline bool hungary(){
for(int i=1;i<=tot;i++){
memset(u,0,sizeof(u));
if(!match(i)) return false;
}
return true;
}
inline void init(){
scanf("%d%d%d",&m,&n,&k);
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
b[i][j]=true;
for(int i=1,j,l;i<=k;i++){
scanf("%d%d",&j,&l);
b[l][j]=false;
}
for(int i=1;i<=m;i++)
for(int j=!(i&1)+1;j<=n;j+=2)
if(b[i][j]){
++tot;
for(int l=1;l<D;l++)
if(b[i+x[l]][j+y[l]])
addedge(tot,(i+x[l]-1)*n+j+y[l]);
}
for(int i=1;i<=m;i++)
for(int j=(i&1)+1;j<=n;j+=2)
if(b[i][j]) sum++;
if(sum==tot&&hungary()) printf("YES\n");
else printf("NO\n");
}
int main(){
freopen("chessboard.in","r",stdin);
freopen("chessboard.out","w",stdout);
init();
fclose(stdin);
fclose(stdout);
return 0;
}