题意:
有M*N的0、1数列,求出怎么反转才能使其全部为零,反转的方式是上下左右受中间
的牵连也会翻转。
思路:
题的模型常常被称为关灯问题,也有点和POJ3276的思想雷同,这里是确定第一行的
排列方式就知道了后边的排列方式。知道这种思想,并且知道位运算是关键。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int Max = 20;
const int dx[5] = {-1,0,0,0,1};
const int dy[5] = {0,-1,0,1,0};
int M,N;
int tile[Max][Max];
int opt[Max][Max];
int flip[Max][Max];
int get(int x,int y)
{
int c = tile[x][y];
for(int d = 0;d < 5; d++) {
int x2 = x + dx[d],y2 = y + dy[d];
if(0 <= x2 && x2 < M && 0 <= y2 && y2 < N) {
c += flip[x2][y2];
}
}
return c%2;
}
int calc()
{
for(int i = 1;i < M; i++) {
for(int j = 0;j < N; j++) {
if(get(i-1,j) != 0) {
flip[i][j] = 1;
}
}
}
for(int j = 0;j < N; j++) {
if(get(M-1,j) != 0) {
return -1;
}
}
int ans = 0;
for(int i = 0;i < M; i++) {
for(int j = 0;j < N; j++) {
ans += flip[i][j];
}
}
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
int ans = -1;
scanf("%d%d",&M,&N);
for(int i = 0;i < M; i++) {
for(int j = 0;j < N; j++) {
scanf("%d",&tile[i][j]);
}
}
for(int i = 0;i < 1<<N; i++) {
memset(flip,0,sizeof(flip));
for(int j = 0;j < N; j++) {
flip[0][N-j-1] = i>>j&1;
}
int num = calc();
if(num >= 0 && (ans < 0 || ans > num)) {
ans = num;
memcpy(opt,flip,sizeof(flip));
}
}
if(ans < 0) {
printf("IMPOSSIBLE\n");
}
else {
for(int i = 0;i < M; i++) {
for(int j = 0;j < N; j++) {
printf("%d%c",opt[i][j],j+1 == N ? '\n' : ' ');
}
}
}
return 0;
}