题目大意:
有一个m*n的棋盘,每个格子上是0或1,每次可以对一个格子做一次翻转操作,将被操作的格子和上下左右4个格子的0/1翻转。问做少做多少次翻转可以将所有格子翻转成0,输出翻转方案。没有方案时输出“IMPOSSIBLE”。解题思路:
枚举第一行的共2^n种翻转方式。枚举第一行的情况相当于确定了整个棋盘的翻转方式,因为在第i行翻转方式确定之后那么在翻转第i+1行时就一定要使第i行全为0。在全部翻转结束后根据判断最后一行是否全为0就可以确定这是不是一个有效解。
代码:
#include <iostream>
#include <cstring>
using namespace std;
int a[20][20],b[20][20];
int f[20][20],ans[20][20],fNum,aNum,n,m;
//翻转
void tranc(int x,int y){
fNum ++;
f[x][y] = 1;
b[x][y] = 1-b[x][y];
b[x-1][y] = 1-b[x-1][y];
b[x+1][y] = 1-b[x+1][y];
b[x][y-1] = 1-b[x][y-1];
b[x][y+1] = 1-b[x][y+1];
}
//记录答案
void record(){
aNum = fNum;
for(int i = 1; i <= m; i ++){
for(int j = 1; j <= n; j ++){
ans[i][j] = f[i][j];
}
}
}
int main()
{
//输入数据
cin >> m >> n;
for(int i = 1; i <= m; i ++){
for(int j = 1; j <= n; j ++){
cin >> a[i][j];
}
}
int t = 1 << n;
aNum = 10000;
//尝试第一行的所有可能
for(int loop = 0; loop < t; loop ++){
for(int i = 1; i <= m; i ++){
for(int j = 1; j <= n; j ++){
b[i][j] = a[i][j];
}
}
fNum = 0;
int moveN = loop;
memset(f,0,sizeof(f));
//翻转第一行
for(int j = 1; j <= n; j ++){
if(moveN & 1) tranc(1,j);
moveN = moveN >> 1;
}
//翻转2-m行
for(int i = 2; i <= m; i ++){
for(int j = 1; j <= n; j ++){
if(b[i-1][j]) tranc(i,j);
}
}
//判断这种翻转方式是否合法
int j;
for(j = 1; j <= n; j ++){
if(b[m][j]) break;
}
if(j == n + 1 && fNum < aNum) record();
}
//不合法
if(aNum == 10000){
cout << "IMPOSSIBLE" << endl;
return 0;
}
//合法
for(int i = 1; i <= m; i ++){
cout << ans[i][1];
for(int j = 2; j <= n; j ++){
cout << " " << ans[i][j];
}
cout << endl;
}
return 0;
}