poj--3279(二进制压缩枚举)

Fliptile
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 5900   Accepted: 2236

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 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 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

解题思路:题目的意思是说有m*n的格子,每个格子里面有一个瓦片,每个瓦片有两面,两面的颜色是不同的,只有两种颜色黑与白。现在牛来反转瓦片使得所有格子的瓦片都为白色,但牛的蹄子比较大,当它反转一个瓦片时,相邻的瓦片也会被反转,问最小反转几次可以使得所有瓦片呈现白色,输出方案,如果有多组方案,输出字典序最小的方案。这里用的是二进制枚举的方法,从0到1<<n这个顺序进行枚举,才能保证字典序最小。反方向则输出的结果不是最小字典序。

首先可以发现一个现象:前一行的1,可以由后一行改变。所以只有最后一行例外。那么只需要枚举第一行可能的状态得到最终整体的情况,求出较满足条件中反转次数最少的方案就行了。感觉用二进制这种方法来枚举太牛了,很是佩服想出想出这种方法的人

代码如下:

#include<stdio.h>
#include<string.h>
int mark[16][16],copy[16][16],change[16][16];
int n,m,sum;
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
void  flip (int i,int j){
	++sum;//记录总反转次数 
	change[i][j]=1;//记录具体的位置的反转次数 
	copy[i][j]=!copy[i][j];//本身反转 
	for(int k=0;k<4;k++){//相邻四个方向反转 
		if(i+dis[k][0]<0||j+dis[k][1]<0||i+dis[k][0]>=m||j+dis[k][1]>=n)continue;
		copy[i+dis[k][0]][j+dis[k][1]] ^= 1;
	}
}
bool ok(int k){
	sum=0;
	//strcpy(copy,mark);
	memcpy(copy,mark,sizeof(copy));//拷贝一个副本 
    memset(change,0,sizeof(change)); 
	for(int i=0;i<n;i++){
		if(k&(1<<(n-1-i)))//巧用与运算判断1的位置 
		 flip(0,i);
	}
	for(int i=1;i<m;i++){
		for(int j=0;j<n;j++){
			if(copy[i-1][j])flip(i,j);//前一行为1,则反转 
		}
	}
	for(int j=0;j<n;j++){
		if(copy[m-1][j])return false;
	}//如果最后一行存在1,说明这种方法不能行 
	return true;
}
int main(){
	while(scanf("%d%d",&m,&n)!=EOF){
		for(int i=0;i<m;i++){
			for(int j=0;j<n;j++){
				scanf("%d",&mark[i][j]);
			}
		}
       int ans,p;
	   ans=(n*m)+1;p=-1;//初始化 
       for(int i=0;i<(1<<n);i++){//枚举第一行所有的情况,一共枚举16次 
       	   if(ok(i)&&sum<ans){
       	   	p=i;ans=sum;
       	   }
       }
//for(int i=(1<<n)-1;i>=0;i--){
//	if(ok(i)&&sum<ans){
//       	   	p=i;ans=sum;
//       	   }
//}//不是字典序最小 
       if(p>=0){
       	   ok(p);
       	   for(int i=0;i<m;i++){
       	   	for(int j=0;j<n;j++){
       	   		printf("%d%c",change[i][j],j<n-1?' ':'\n');
       	   	}
       	   }
       }
       else printf("IMPOSSIBLE\n");
	}
	
	return 0;
}



你可能感兴趣的:(poj--3279(二进制压缩枚举))