题目链接:点击打开链接
题意:一个棋盘,n*m的大小,棋盘上有一些窟窿,让你往棋盘上放1*2的长方形。横着放,竖着放都可以,但是窟窿的地方不能放,只要你能填满棋盘就输出YES,填不满就输出NO。
题解:最开始=-=看到alice和bob,就以为是博弈论,博了半天没博出来,然后看到1*2的方格就又想到了状压DP,又压了半天=-=。然后放弃了。
搜搜题解才发现,是二分图匹配问题。(惊喜的一比)
我们先给给个棋盘从上到下,从左到右依次标号。
如4*4的图如下
(0,0) (0,1)(0,2)(0,3)
(1,0) (1,1)(1,2)(1,3)
(2,0) (2,1)(2,2)(2,3)
(3,0) (3,1)(3,2)(3,3)
我们观察会发现,如果一个格子的行号和列号加起来为奇数,那与它相邻的格子的行号和列号加起来一定是偶数,如果一个格子的行号和列号加起来为偶数,那与它相邻的格子的行号和列号加起来一定是奇数。这样,我们就可以把这张棋盘分为奇数集合和偶数集合。加边的时候,我们只需要加它上面或左面就可以了。(精髓所在)
然后建好图跑一遍二分图算法就行了。
博主用的是匈牙利算法,博主不打算讲。因为博主是感性的认知,没有理性的论证。所以推荐一个博客:https://blog.csdn.net/dark_scope/article/details/8880547
这个博客通俗易懂。
上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int M = 33 * 33, N = M * M;
int Head[M], top;
int match[M];
bool use[M],map[M][M];
int m, n, k;
struct node {
int v;
int next;
}edge[N];
void add(int u, int v) //奇 + 偶
{
edge[top].v = v;
edge[top].next = Head[u];
Head[u] = top++;
}
bool find(int u){
int temp;
for(int i = Head[u] ; i != - 1; i = edge[i].next){
temp = edge[i].v;
if(!use[temp]){
use[temp] = true;
if(match[temp] == -1 || find(match[temp])){
match[temp] = u;
return true;
}
}
}
return false;
}
int sum(int n){
int sumall = 0;
for(int i = 0 ; i < n ; i ++){
memset(use,0,sizeof(use));
if(find(i))
sumall++;
}
return sumall;
}
int main()
{
int x, y;
while(scanf("%d%d%d", &m, &n, &k) != EOF)
{
if((m * n - k) & 1)
{
printf("NO\n");
continue;
}
top = 0;
memset(Head, -1, sizeof(Head));
memset(match, -1, sizeof(match));
memset(map, true, sizeof(map));
for(int i = 0; i < k; ++i)
{
scanf("%d%d", &y, &x); //先y后x
map[x - 1][y - 1] = false;
}
for(int i = 0; i < m; ++i)
{
for(int j = 0; j < n; ++j)
{
if(map[i][j])
{
if(i - 1 >= 0 && map[i - 1][j]) //上
{
if((i + j) & 1)
add(i * n + j, (i - 1) * n + j);
else
add((i - 1) * n + j, i * n + j);
}
if(j - 1 >= 0 && map[i][j - 1]) //左
{
if((i + j) & 1)
add(i * n + j, i * n + (j - 1));
else
add( i * n +(j - 1) , i * n + j);
}
}
}
}
for(int i = 0 ; i < n * m ; i ++){
for(int k = Head[i] ; k != -1 ; k = edge[k].next)
cout << i << " " << edge[i].v << endl;
}
printf("%s\n", sum(m * n) == ((m * n - k) / 2) ? "YES" : "NO");
}
return 0;
}