POJ 3279 - Fliptile(枚举+递推)

Fliptile

Time Limit: 2000MS

Memory Limit: 65536K

Description

Farmer John knows that an intellectuallysatisfied cow is a happy cow who will give more milk. He has arranged a brainyactivity for cows in which they manipulate an M × N grid(1 ≤ M ≤ 15; 1 ≤ N ≤ 15) of square tiles,each of which is colored black on one side and white on the other side.

As one would guess, when a single whitetile is flipped, it changes to black; when a single black tile is flipped, itchanges to white. The cows are rewarded when they flip the tiles so that eachtile has the white side face up. However, the cows have rather large hooves andwhen they try to flip a certain tile, they also flip all the adjacent tiles(tiles that share a full edge with the flipped tile). Since the flips aretiring, the cows want to minimize the number of flips they have to make.

Help the cows determine the minimum numberof flips required, and the locations to flip to achieve that minimum. If thereare multiple ways to achieve the task with the minimum amount of flips, returnthe one with the least lexicographical ordering in the output when consideredas a string. If the task is impossible, print one line with the word"IMPOSSIBLE".

Input

Line 1: Twospace-separated integers: M and N 
Lines 2..M+1: Line i+1 describes the colors (left to right)of row i of the grid with N space-separated integers which are1 for black and 0 for white

Output

Lines 1..M:Each line contains N space-separated integers, each specifyinghow many times to flip that particular location.

Sample Input

4 4

1 0 0 1

0 1 1 0

0 1 1 0

1 0 0 1

Sample Output

0 0 0 0

1 0 0 1

1 0 0 1

0 0 0 0

 

 

【题意】

给定一个M*N的01矩阵代表M*N个方格(0表示白色,1表示黑色),每个格子都可以翻转正反面,翻转之后颜色相反,但是翻转1个格子时,与它邻接的上下左右4个格子也会被翻转。现在要求用最少的步数把所有格子翻转成白色,输出对应的操作矩阵(1表示该位置需要翻转,0表示不需要),有多解则输出字典序最小的一组解。若无解输出“IMPOSSIBLE”。(1<=M,N<=15)

 

【思路】

直接暴力枚举所有位置翻转或不翻转时间复杂度是O(2^MN),所以行不通。这里用到了一个经典的方法就是枚举第一行,推算剩余行。我们可以把矩阵第一行的翻转情况按照字典序递增枚举,确定了第一行之后,这时第一行第一列的颜色情况只取决于它下面的第二行第一列格子的翻转情况,而第一行第一列的颜色必须是白色,所以就能计算出第二行第一列格子的翻转情况,同理可依次推算后面的格子的翻转情况直到推完最后一行。最后判断最后一行是否全部为白色即可判断当前解是否为可行解。虽然思路明确,但是这道题的细节比较多,很容易出错,要在不看题解的情况下多练习几次。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxm = 16, maxn = 16;

//左,上,中,右,下
const int dx[5] = { 0, -1, 0, 0, 1 };//行
const int dy[5] = { -1, 0, 0, 1, 0 };//列

int m, n;
int tile[maxm][maxn];//输入
int opt[maxm][maxn];//记录最优解
int flip[maxm][maxn];//中间结果

int calc() {//推算当前情况,并返回最少的翻转次数
	int res = 0;
	for (int j = 0; j < n; j++) if (flip[0][j]) res++;

	for (int i = 1; i < m; i++) {//推算第二行至最后一行
		for (int j = 0; j < n; j++) {
			int tmp = tile[i - 1][j];
			for (int k = 0; k < 4; k++) {
				int x = (i - 1) + dx[k];//注意是通过上一行的格子必须是白色来推这一行是否翻转
				int y = j + dy[k];
				if (x >= 0 && x < m && y >= 0 && y < n)
					tmp += flip[x][y];
			}
			if (tmp & 1) {//奇偶情况和开关问题1类似
				res++;
				flip[i][j] = 1;
			}
			else
				flip[i][j] = 0;
		}
	}

	for (int j = 0; j < n; j++) {//判断最后一行是否符合要求
		int tmp = tile[m - 1][j];
		for (int k = 0; k < 4; k++) {
			int x = (m - 1) + dx[k];
			int y = j + dy[k];
			if (x >= 0 && x < m && y >= 0 && y < n) 
				tmp += flip[x][y];
		}
		if (tmp & 1) return -1;
	} 
	return res;
}

int main() {
	while (scanf("%d%d", &m, &n) == 2) {
		for (int i = 0; i < m; i++)
			for (int j = 0; j < n; j++)
				scanf("%d", &tile[i][j]);

		int res = -1;
		for (int i = 0; i < 1 << n; i++) {//按字典序递增枚举
			memset(flip, 0, sizeof(flip));
			for (int j = 0; j < n; j++) {
				flip[0][j] = i >> (n - 1 - j) & 1;
			}
			int num = calc();
			if (num >= 0 && (res == -1 || num < res)) { //这里的判断方法值得学习
				res = num;
				memcpy(opt, flip, sizeof(opt));
			}
		}

		if (res >= 0) {
			for (int i = 0; i < m; i++)
				for (int j = 0; j < n; j++)
					printf("%d%c", opt[i][j], j == n - 1 ? '\n' : ' ');
		}
		else printf("IMPOSSIBLE\n");
	}
	return 0;
}


posted @ 2017-10-29 00:04  不想吃WA的咸鱼  阅读(105)  评论(0编辑  收藏  举报