思路:
有第i+1行格子的反转来改变第i行中黑格子的颜色。
先将第一行格子的所有反转方式都列出来,共2^n种,使用二进制的方式枚举(详见代码);
将第一行反转后,若第一行中第j个格子仍为黑,则将第二行第j个格子反转(因为第一行已经反转完毕,所以只有第二行第j个格子的反转能改变它的颜色);
第二行全部判断是否反转后,再第三行,方法同上,依此类推。
直到倒数第二行全变成白色后,判断最后一行格子的颜色,若全部是白色,则这种反转方式成立,记录其反转次数;否则不成立。
更换第一行的反转方式,继续上述过程,直到2^n种全部历遍。
AC代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
int m, n, dir[16][16], f[16][16], ans[16][16];
int dx[5]={1, -1, 0, 0, 0}, dy[5]={0, 0, 1, -1, 0}; //注意dx=0且dy=0的情况,格子自身也会翻转
int get(int x, int y){ //判断格子(x,y)此时的颜色,黑色为1,白色为0
int res = dir[x][y];
for(int i = 0; i < 5; i++){
int x1 = x + dx[i], y1 = y + dy[i];
if(x1 >= 0 && y1 >= 0 && x1 < m && y1 < n) res += f[x1][y1];
}
return res % 2;
}
int calc(){
int res = 0;
//从第二行开始判断是否需要翻转
for(int i = 1; i < m; i++){
for(int j = 0; j < n; j++){
if(get(i - 1, j)) f[i][j] = 1;
}
}
for(int j = 0; j < n; j++){
if(get(m - 1, j) != 0) return -1;
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
res += f[i][j];
}
}
return res;
}
int main() {
scanf("%d %d", &m, &n);
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
scanf("%d", &dir[i][j]);
}
}
int res = -1;
for(int i = 0; i < (1 << n); i++){ //第一行有2^n种翻法
memset(f, 0, sizeof(f));
for(int j = 0; j < n; j++){
f[0][j] = (i >> j) & 1; //取i/j^2的末位判断(0,j)是否翻转
}
int num = calc();
if(num >= 0 && (res < 0 || res > num)){
res = num;
memcpy(ans, f, sizeof(f));
}
}
if(res < 0) printf("IMPOSSIBLE\n");
else{
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
printf("%d", ans[i][j]);
if(j != n - 1) printf(" ");
}
printf("\n");
}
}
return 0;
}