POJ-2446 Chessboard

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

题目大意:

给你一个m*n的棋盘,其中有k个小洞,现在给你1*2的纸片,如果能恰好覆盖没有洞的全部格子,而且每个格子不能被覆盖2次,一张纸也必须是1*2规模的,这样就输出YES。

解题思路:

这道题是搜二分图搜到的。但是竟然一点也看不出来哪里能用二分图最大匹配来做。只好去搜题解了。。。

思路是:

把这个棋盘看成一个类似国际象棋的棋盘,黑白相间的那种。任何2个相邻的格子肯定是不同颜色的(黑和白)。经过研究发现,如果一个格子的行号和列号加起来为奇数,那与它相邻的格子的行号和列号加起来一定是偶数,如果一个格子的行号和列号加起来为偶数,那与它相邻的格子的行号和列号加起来一定是奇数。

这样,我们就可以把这张棋盘分为奇数集合和偶数集合。加边的时候,我们只需要加它上面或左面就可以了。。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int M = 33 * 33, N = M * M;
int Head[M], Next[N], Key[N], top;
int match[M];
bool use[M], map[M][M];
int m, n, k;

void add(int u, int v) //奇 + 偶
{
	Key[top] = v;
	Next[top] = Head[u];
	Head[u] = top++;
}

bool find(int u)
{
	int temp;
	for(int i = Head[u]; i != -1; i = Next[i])
	{
		temp = Key[i];
		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, false, 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);
					}
				}
			}
		}
		printf("%s\n", sum(m * n) == ((m * n - k) / 2) ? "YES" : "NO");
	}
	return 0;
}


你可能感兴趣的:(POJ-2446 Chessboard)