POJ 3279 Fliptile.cpp

Description:

有一个n*m的格子,每个格子都有黑白两面(0表示白色,1表示黑色)。我们需要把所有的格子都反转成黑色,每反转一个格子,它上下左右的格子都会跟着反转。请求出用最小步数完成反转时每个格子反转的次数。有多个解时,输出字典序最小的一组。

Analysis:

一个格子没有必要翻转两次。
如果第一行翻转方法确定了,那么递推下面所有行就都确定了。
先确定第一行翻转方法O(2^n),接下来每一行,如果某一个格子的上一行格子是黑色的,那么就翻转这个格子。
时间复杂度O(nm2^n)

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define White 0
#define Black 1
using namespace std;
const int N = 20;
const int dx[5] = {-1,0,0,0,1};
const int dy[5] = {0,-1,0,1,0};
int tile[N][N],flip[N][N],opt[N][N];
int m,n;
int getColour(int x,int y)
{
	int c = tile[x][y];
	for(int d = 0;d < 5;++d)
	{
		int x1 = x + dx[d],y1 = y + dy[d];
		if((0 <= x1 && x1 < m) && (0 <= y1 && y1 < n))
		{
			c += flip[x1][y1];
		}
	}
	return c % 2;
}
int calc()
{
	for(int i = 1;i < m;++i)
	{
		for(int j = 0;j < n;++j)
		{
			if(getColour(i-1,j) == Black)
			{
				flip[i][j] = 1;
			}
		}
	}
	for(int i = 0;i < n;++i)
	{
		if(getColour(m - 1,i) == Black)
		{
			return -1;
		}
	}
	int ans = 0;
	for(int i = 0;i < m;++i)
	{
		for(int j = 0;j < n;++j)
		{
			ans += flip[i][j];
		}
	}
	return ans;
}
void solve()
{
	int ans = 10000;
	int flag = 1;
	for(int i = 0;i < (1 << n);++i)
	{
		memset(flip,0,sizeof(flip));
		for(int j = 0;j < n;++j)
		{
			flip[0][n - j - 1] = (i >> j) & 1;
		}
		int num = calc();
		if(num < ans && num > 0)
		{
			ans = num;
			flag = 0;
			memcpy(opt,flip,sizeof(flip));
		}
	}
	if(flag)
	{
		printf("IMPOSSIBLE\n");
	}
	else{
		for(int i = 0;i < m;++i)
		{
			for(int j = 0;j < n;++j)
			{
				printf("%d%c",opt[i][j],j + 1 == n ? '\n' : ' ');
			}
		}
	}
}
int main()
{
	scanf("%d%d",&m,&n);
	for(int i = 0;i < m;++i)
	{
		for(int j = 0;j < n;++j)
		{
			scanf("%d",&tile[i][j]);
		}
	}
	solve();
	return 0;
}
posted @ 2019-07-12 20:19  Zforw  阅读(36)  评论(0编辑  收藏  举报