POJ-----3279暴力枚举

Fliptile
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 7394 Accepted: 2787

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,如果翻转一个数,那么它的本身和上下左右5个数都会翻转,翻转即从0变成1,1变成0,问能不能把所有的数都翻转成0,若有多种翻转方法,就输出字典序最小的那个,这题真是烧脑,没有什么具体思路,只能暴力枚举了。

从第一行开始,第一行可以通过翻转任意的一个或几个使数字产生任意的组合,而每个数字只有1,0两种状态(如:1001可以翻转中间两个变成0110,翻转第一个可以变成0101),所以无论怎么翻转一共有2^n种状态,只能一个一个枚举了,刚看到15*15感觉挺小,这一暴力就是2^15,再大点就炸了。

2^n种状态(先说思路,实现下面再说),每种状态就是一串固定的第一行数字,而每个数字只能影响它的上下左右,而第一行固定了,要想把第一行全部翻转成0,第二行要翻转的数字也是固定的,类推……(ps:最后一行必须全是0,也就是最后一行不能翻转奇数次,偶数次等于没翻,因为接下来没有下一行再把它的1转成0了)要想全部翻转成0,第一行固定,也就是所有的都固定了,所以遍历第一行所有情况然后检查最后一行就可以了。

然后就是如何实现遍历了,这就用到二进制了

for(int i = 0; i < (1 << n); i++){
	for(int j = 1; j <= n; j++){
		sim[1][j] = (i >> (j-1)) & 1;
	}
}


就用到这段代码了,储存第一行的所有情况,1<<n就是1的二进制左移n位也就是1*(2^n),这个比一次次*2快,然后就是(i>>(j-1)) & 1, 就是i的二进制右移j-1位再按位与1运算

比如i=0,二进制是0(省略了前导0), n是4,通过上述计算得出来四个数0000,

i=1,二进制是1,得出来1000

i=2,二进制是10,得出来0100

i=3,二进制是11,得出来1100

……

依次就可以遍历出来所有情况,然后就可以计算了-----涨姿势~~~


#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 1e9
using namespace std;
int n, m, ans, t, res, cnt;
int map[40][40], sol[40][40], sim[40][40];
int mov[5][2] = {0, 0, 0, 1, 0, -1, 1, 0, -1, 0};
int get(int x, int y){
	int c = map[x][y];
	for(int i = 0; i < 5; i++){
		int x1 = x + mov[i][0], y1 = y + mov[i][1];
		c += sim[x1][y1];
	}
	return c % 2;
}
int solve(){
	for(int i = 2; i <= m; i++){
		for(int j = 1; j <= n; j++){
			if(get(i-1, j) == 1){
				sim[i][j] = 1;
			}
		}
	}
	for(int i = 1; i <= n; i++){
		if(get(m, i)){
			return -1;
		}
	}
	int cnt = 0;
	for(int i = 1; i <= m; i++){
		for(int j = 1; j <= n; j++){
			cnt += sim[i][j];
		}
	}
	return cnt;
}
int main(){
	t = -1;
	scanf("%d%d", &m, &n);
	for(int i = 1; i <= m; i++){
		for(int j = 1; j <= n; j++){
			scanf("%d", &map[i][j]);
		}
	}
	for(int i = 0; i < (1 << n); i++){
		memset(sim, 0, sizeof(sim));
		for(int j = 1; j <= n; j++){
			sim[1][j] = (i >> (j-1)) & 1;
		}
		ans = solve();
		if(ans >= 0 && (t < 0 || t > ans)){
			t = ans;
			memcpy(sol, sim, sizeof(sim));
		}
	}
	if(t == -1){
		printf("IMPOSSIBLE\n");
	}
	else{
		for(int i = 1; i <= m; i++){
			for(int j = 1; j <= n; j++){
				printf("%d%c", sol[i][j], j == n ? '\n' : ' ');
			}
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值