状态压缩(2)+模拟枚举(2)--poj3279(能力题)

  Fliptile

                                                       Time Limit:2000MS    Memory Limit:65536KB    64bit IO Format:%lld & %llu

Description

Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy activity for cows in which they manipulate anM ×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 white tile is flipped, it changes to black; when a single black tile is flipped, it changes to white. The cows are rewarded when they flip the tiles so that each tile has the white side face up. However, the cows have rather large hooves and when 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 are tiring, the cows want to minimize the number of flips they have to make.

Help the cows determine the minimum number of flips required, and the locations to flip to achieve that minimum. If there are multiple ways to achieve the task with the minimum amount of flips, return the one with the least lexicographical ordering in the output when considered as a string. If the task is impossible, print one line with the word "IMPOSSIBLE".

Input

Line 1: Two space-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 are 1 for black and 0 for white

Output

Lines 1.. M: Each line contains N space-separated integers, each specifying how 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


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

        做这题之前,我们首先需要理解一个现象:除了最后一行,其他任何一行上的1都可以通过下一行的翻转转换成0。也就是说,除了最后一行外,我们总是可以通过翻转,将前n-1行翻成全0。只要按照这样的原则:对于某一个位置x,如果它的上一个是1,就翻转它,如果是0,就不翻转。知道了这个,我们应该就能想到,第一行的翻法直接就决定了后面的所有的翻法,这就是我们解决这道题目的思路,即枚举第一行所有可能的翻法,这里,如果用循来枚举,未免太浪费时间了,所以,这里采取了一种二进制压缩的方法。
        首先让我们来学习一下左移<<的概念,其实很好理解,就是将一个数转换为二进制,然后向左移动若干位,然后在多出来的位置上补零,比如,对于5<<2,5的二进制为101,左移两位就是10100,那么最后的结果就是20,。
利用这个特点,我们可以通过使用一个特殊的数字1,来解决这个枚举问题。枚举的第一步就是确定到底要改变哪几位数,联想到二进制,我们可以这样处理,就题目的测试数据而言,一行有四个数,用一个二进制数xxxx表示,易知,xxxx一共有2^4种排列,其实也是1<<4,之所以这样写,是因为这比pow要快,所以,我们让k从0开始枚举到15,也就是0000到1111,然后规定,只要是带1的位置,就要翻转这个位置的数字,问题又来了,怎么知道哪一位是1,呢,这里还是用到了二进制,即与运算,我们让k分别与1000,0100,0010,0001进行与运算,分别对应不同的位,如果结果不是1,说明这一位上不是0,是不是很巧妙,当然,那四个值依然是通过1的左移来计算出来的,核心的东西讲完了,下面上代码:

#include
#include
int M,N;
int map[20][20];
int vis[20][20];  //保存每一格的翻转次数
int t[20][20];   //临时数组用来保存中间结果
int d[4][2]={0,-1,-1,0,0,1,1,0};
int cnt;
void Flip(int i,int j){       //翻转
	cnt++;                 //次数加1
	t[i][j]=!t[i][j];      //先翻转自己
	vis[i][j]=1;
	for(int k=0;k<4;k++){    //翻转上下左右四个方向的格子
		int x=i+d[k][0];
		int y=j+d[k][1];
		if(x<0||y<0||x>=N||y>=M)   //越界判断
			continue;
		t[x][y]=!t[x][y];     //翻转
	}
}
bool Judge(int num){    //对于第一行的每一种翻转情况,判断是否能产生矩阵全翻转为0的结果
	int i,j;
	cnt=0;       
	memcpy(t,map,sizeof(t));     //初始化临时数组
	for(i=0;i=0){           //最后找到的就是最少的翻法,模拟一遍,然后输出  
			Judge(p);
			for(i=0;i


你可能感兴趣的:(【状态压缩】,【模拟,枚举,贪心,二分,尺取】)