分析
1.操作的顺序对最终结果无影响;
2.操作偶数次无意义,由于1,所以只有1次操作和0次操作的区别,即:对于每个位置,只有操作一次或者不操作两种可能;
3.每次操作会改变上下左右中共5个位置;
4.基于以上三点,对于顺序操作的相邻行(列),前一行(列)的状态决定下一行(列)是否需要操作。比如第一行01001,则第二行对应1,3,4需要操作,使得第一行的1,3,4位置变为1。所以当第一行的操作确定了,后面的操作是固定的,这是为什么只枚举第一行的原因。枚举第一列也可以达到同样的效果。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 500;
int n;
int on[N+1][25];
int minO;
// 朴素操作,无技巧可言。。。
void change(int row, int col) {
// 本身
on[row][col] = !on[row][col];
// 上
if(col-5 >= 0) on[row][col-5] = !on[row][col-5];
// 下
if(col+5 < 25) on[row][col+5] = !on[row][col+5];
// 左
if(col%5 != 0) on[row][col-1] = !on[row][col-1];
// 右
if((col+1)%5 != 0 ) on[row][col+1] = !on[row][col+1];
}
// 当第一行操作完后,判断是否有解
void judge(int row, int oper) {
int opera = oper; // 第一行
for(int i = 5; i <25; i++){
if(on[row][i-5] == 0) {
// 上一行对应列为0,则下一行对应列操作一下,使得上一行对应列置1
change(row, i);
opera++;
}
}
for(int i = 20; i < 25; i++) {
if(on[row][i] == 0)
return;
}
if(opera < minO)
minO = opera;
}
void dfs(int row, int pos, int oper) {
// 1.递归边界
if (pos == 5) {
judge(row, oper);
return;
}
// 2.分支
// 2.1 状态记录
int tmp[25];
for(int i = 0; i < 25; i ++) {
tmp[i] = on[row][i];
}
// 2.2 状态转移,pos改变, opera+1
change(row, pos);
dfs(row, pos+1, oper+1);
// 2.3 状态恢复
for(int i = 0; i < 25; i ++) {
on[row][i] = tmp[i];
}
// 2.4 状态转移
dfs(row, pos+1, oper);
}
int main(int argc, char** argv) {
cin >> n;
for(int i = 0; i < n; i++) {
for(int j = 0; j <5; j++){
string tmp;
cin >> tmp;
for(int k = 0; k < 5; k++)
on[i][j*5+k] = tmp.at(k)-'0';
}
}
for(int i = 0; i < n; i++) {
minO = 0x3fffffff;
dfs(i,0,0);
if (minO > 6 ) minO = -1;
cout << minO << endl;
}
return 0;
}