题目:
思想:
- 只能从下一行去改变上一行的状态,因此第一行的状态即全局状态。
- 对第一行,存在00000~11111共2^5=32种turn的方案,这32种方案会导致我们对输入的原始第一行数据进行32种不同的开关灯操作,某位上为1则对原始数据第一行该位上进行turn操作。随后对第一行每一种方案再进行枚举。
- 对5×5的前4行的每一行,若某个元素为‘0’,则下一行的该位置需要按一下(turn)。
- 检查到最后一行,若最后一行全都为1,则可以使灯全亮。
- 若某方案的turn操作次数大于6,则不符合题意。
- 每个方案尝试结束后,还原原始数据g数组。
#include <iostream>
#include <cstring>
using namespace std;
const int INF=1000000;
char g[10][10];
int dx[5]={0,-1,0,1,0},dy[5]={0,0,1,0,-1};
//向量表示:dx表示行变化,中0上-1右0下+1左0
//y表示列变化,中0上0右+1下0左-1
void turn(int x,int y){ //对某灯包括其周围4个方向的灯进行开关
for(int i=0;i<5;i++){
int a=x+dx[i],b=y+dy[i];
if(a>=0&&a<5&&b>=0&&b<5){ //保证在5×5范围内进行turn
g[a][b]^=1; //'0'==48,偶数异或1为偶数+1。'1'==49,奇数异或1为奇数-1。实现开关。
}
}
}
int work(){
int ans=INF; //Infinity 无穷大
for(int k=0;k<1<<5;k++){ //枚举00000~11111,2^5种方案
int res=0;
char backup[10][10];//备份g数组
memcpy(backup,g,sizeof g);//cstring库,一般用于字符数组复制。void* void* size_t(要复制的字节数)
for(int j=0;j<5;j++){
if(k >> j & 1){
res++; //操作次数+1
turn(0,j); //对第一行进行turn操作,32种turn方案逐次尝试
}
}
for(int i=0;i<4;i++){
for(int j=0;j<5;j++){
if(g[i][j]=='0'){
res++;
turn(i+1,j);//对下一行对应位置turn,使得当前行被点亮
}
}
}
bool flag=true;
for(int j=0;j<5;j++){
if(g[4][j]=='0'){ //最后一行并非全'1'
flag=false;
break;
}
}
if(flag) ans=min(ans,res);
memcpy(g,backup,sizeof backup); //还原原始数据,尝试下一种第一行turn方案
}
if(ans>6) ans=-1;
return ans;
}
int main(){
int n;
cin >> n;
while(n--){
for(int i=0;i<5;i++){
cin >> g[i];
}
cout << work() << endl;
}
return 0;
}
END