做法思路:
改变一个灯,它的上下左右位置的等也要随之改变
1亮,0灭
先枚举第一行的状态,都是按或不按两种情况,2^5按法
第一行按过以后就不能再按了,开始按第二行,第二行按哪个格子是由第一行暗掉的格子唯一确定的,
因为十字型,能改变上面一个格子的只在它的正下方。
总的来说就是每一行开关的操作都被前一行灯的亮灭状态唯一确定。
到倒数第二行按完时,若最后一行全是亮的话,则满足,否则失败
时间复杂度:32205*500(500组数据,递推20格,一次5)一千六百万
注:不要忘记将计数组恢复原状!!!memcpy(g, backup, sizeof g);
代码
#include<iostream>
#include<cstring>
using namespace std;
const int INF = 1000;
char g[10][10];//灯的状态
int dx[5] = { 0,-1,0,1,0 }, dy[5] = { 0,0,1,0,-1 };//向量来表示,中、上、右、下、左
void turn(int x, int y)//按灯方法
{
for (int i = 0; i < 5; i++) {
//枚举以x、y为中心的五个数
int a = x + dx[i], b = y + dy[i];
//检查数是否在范围内
if (a >= 0 && a < 5 && b >= 0 && b < 5)
{
//0则变1,1则变0
//g[a][b] = '0' + ('1' - g[a][b]);
g[a][b] ^= 1;//亦或
}
}
}
int work() {
int ans = INF;
for (int k = 0; k < 1 << 5; k++)// 1 << 5代表二进制左移五位,就是十进制数的32,2^5每一行按法的可能
{
int res = 0;//当前方案的操作数
char backup[10][10];//枚举很多次,故copy一份
memcpy(backup, g, sizeof g);
for (int j = 0; j < 5; j++)
{
if (k >> j & 1) //先将k右移i位,再与1进行按位与运算,都为1则1,否则都是0
{
res++;
turn(0, j);
}
}
//递推前n-1行
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 5; j++)
{
if (g[i][j] == '0')
{
res++;
turn(i + 1, j);//若当前位为0,则将对应的下一行按一下
}
}
}
//判断最后一行是否全亮
bool is_successful = true;
for (int j = 0; j < 5; j++)
{
if (g[4][j] == '0')//若最后一行有暗的灯,则不符合
{
is_successful = false;
break;
}
}
if (is_successful) ans = min(ans, res);
memcpy(g, backup, sizeof g);//将计数组恢复原状,重新枚举下一次的状态
}
if(ans > 6) ans = -1;
return ans;
}
int main() {
int T;//多少组数据
cin >> T;
while (T-- ) {
for (int i = 0; i < 5; i++) cin >> g[i];//把初始化的灯状态写入
cout << work() << endl;
}
return 0;
}