POJ 3279 Fliptile(枚举)

题目链接

http://poj.org/problem?id=3279

题意

给定M*N的瓦片矩阵,瓦片正反为不同的颜色:1(黑色),0(白色)。当触摸一个瓦片时,这块瓦片和其上下左右的瓦片都会翻动(即黑色变为白色,白色变为黑色)。问是否有一种触摸方法使所有瓦片都是白色面朝上。若存在则输出翻动次数最少的方法,若不能输出"IMPOSSIBLE"

思路

  1. 首先要明白,触摸同一个瓦片两次相当于没有触摸这块瓦片,所以对于任意一块瓦片,触摸的次数不是0就是1(触摸的次数,不是翻动的次数)
  2. 解题方法是枚举,但并不是枚举每个瓦片触摸的次数,只枚举第一行的即可,当确定第一行瓦片触摸的次数后,2m行瓦片触摸的次数是确定的。对于2m行的任意一个瓦片(x, y),若瓦片(x-1,y)是黑色的,那么就触摸一次改瓦片(x,y),这样瓦片(x-1,y)就变成了白色,同理通过控制是否触摸(x+1, y)来确保瓦片(x,y)的颜色是白色。这样,最后查看最后一行是否全部为白色即可确定这种触摸方法是否可行。
  3. 枚举第一行瓦片触摸次数的方法是用二进制0,1表示相应位数上瓦片的触摸次数

代码

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>

using namespace std;

const int INF = 0x3f3f3f3f;

int tile[16][16];//输入的瓦片矩阵
int time[16][16];//枚举的时候用到的,保存每块瓦片触摸的次数
int tmp[16][16];//也是枚举的时候用到的,保存触摸后瓦片朝上的颜色
int final_ans[16][16];//保存最优解
int m, n;
int ans, cur;
//顺序:左,当前,右,上
int dx[] = {0, 0, 0, -1};
int dy[] = {-1, 0, 1, 0};

bool judge(int x, int y){
	return x >= 0 && x < m && y >= 0 && y < n;
}

int solve(){
	memcpy(tmp, tile, sizeof tile);
	//执行第一行进行的翻转
	int x = 0, tx, ty;
	for (int y = 0; y < n; ++y){   //瓦片(0, y)
		for (int i = 0; i < 4; ++i){
			tx = x + dx[i];
			ty = y + dy[i];
			if (judge(tx, ty)){
				tmp[x][y] += time[tx][ty];
			}
		}
		tmp[x][y] %= 2;
	}
	//从第二行开始,将上一行所有瓦片变为白色
	for (x = 1; x < m; ++x){
		//若(x-1,y)是黑色,翻转(x,y)
		for (int y = 0; y < n; ++y){
			time[x][y] = tmp[x - 1][y];
		}
		//执行翻转
		for (int y = 0; y < n; ++y){
			for (int i = 0; i < 4; ++i){
				tx = x + dx[i];
				ty = y + dy[i];
				if (judge(tx, ty)){
					tmp[x][y] += time[tx][ty];
				}
			}
			tmp[x][y] %= 2;
		}
	}
	//检查最后一行瓦片是否全部为白色
	x = m - 1;
	for (int y = 0; y < n; ++y){
		if (tmp[x][y]){
			return -1;
		}
	}
        //统计翻动的次数
	int s = 0;
	for (int i = 0; i < m; ++i){
		for (int j = 0; j < n; ++j){
			s += time[i][j];
		}
	}
	return s;
}

int main(){
	while (~scanf("%d%d", &m, &n)){
		//读入数据
		for (int i = 0; i < m; ++i){
			for (int j = 0; j < n; ++j){
				scanf("%d", &tile[i][j]);
			}
		}
		ans = INF;
		//枚举第一行瓦片翻转的次数
		int ed = 1 << n;
		for (int i = 0; i < ed; ++i){
			memset(time, 0, sizeof time);
			for (int j = 0; j < n; ++j){
				if (i & (1 << j)){
					time[0][n - j - 1] = 1;
				}
			}
			cur = solve();
			if (cur >= 0 && cur < ans){
				ans = cur;
				memcpy(final_ans, time, sizeof time);
			}
		}
		if (ans == INF){
		   printf("IMPOSSIBLE\n");
		}
		else{
			for (int j = 0; j < m; ++j){
				for (int k = 0; k < n; ++k){
					printf("%d ", final_ans[j][k]);
				}
				printf("\n");
			}
		}
	}
	return 0;
}
posted @ 2017-08-20 19:53  LuSimon  阅读(131)  评论(0编辑  收藏  举报