#include <iostream>
#include <memory>
#include <cstring>
#include <string>
using namespace std;
char oriLights[5];
char Lights[5];
char result[5];
int GetBit(char c,int i)
{
return (c >> i) & 1;
}
void SetBit(char &c,int i,int v)
{
if (v) {
c |=(1 << i);
}
else {
c &= ~(1 << i);
}
}
void FlipBit(char &c,int i)
{
c ^= (1 << i);//与1进行异或运算则翻转
//与0进行异或运算则不变
}
void OutputResult(int t,char result[])
{
cout << "PUZZLE #" << t << endl;
for (int i = 0; i < 5;i++) {
for (int j = 0; j < 6;j++) {
cout << GetBit(result[i], j);
if (j<5)
cout << " ";
}
cout << endl;
}
}
int main()
{
int T;
cin >> T;
for (int t = 1; t <= T;t++) {
memset(oriLights, 0, sizeof(oriLights));
for (int i = 0; i < 5;i++) {
for (int j = 0; j < 6;j++) {
int s;
cin >> s;
SetBit(oriLights[i], j, s);
}
}
for (int n = 0; n < 64;n++) {
memcpy(Lights, oriLights, sizeof(oriLights));//第i行的开关状态
int switchs = n;
for (int i = 0; i < 5;i++) {
result[i] = switchs;//第i行的开关方案
for (int j = 0; j < 6;j++) {
if (GetBit(switchs,j)) {//如果i行j位为真就翻转
if (j>0)
FlipBit(Lights[i], j-1);
FlipBit(Lights[i], j);
if(j<5)
FlipBit(Lights[i], j + 1);
}
}
if (i<4) {
Lights[i + 1] ^= switchs;//改第i+1行的灯,还是异或运算的应用
//如果上一行按了,势必会影响下一行
}
switchs = Lights[i];//为了灭掉第i行还未被灭掉的灯,所以将i+1行的开关方案设置为第i行灯亮的情况
//1表示按下,0表示不按,1表示灯亮,0表示灯灭,正好一一对应
}
if (Lights[4]==0) {
OutputResult(t, result);
break;
}
}
}
getchar();
getchar();
return 0;
}
Lights[ i ] 是结果数组,oriLights[ i ] 是读入时的矩阵,result [ i ] 是存翻转方案的矩阵。
对这个题用的是枚举的思路,但是如果全部枚举的话就是 2 的 30 次方,所以我们只枚举第一行,就是2 的 6 次方。
因为当你的第一行按下之后,之后的四行都是要为第一行服务的,所以也就是说为了让第一行为 0 ,就要让第二行的熄灯方案
为第一行还亮着的灯的位置。
只有这样才能把之前第一行未熄灭的灯给熄灭掉,但是代码中并没有再次修改上一行灯的状态,我们只是假修改一下。
实际上并未修改,因为这样并不会影响我们继续执行下一行的熄方案,我们只是知道它已经被修改了就行了。
这样的话第二行就确定了,因此也就没有必要去枚举了。然后第三行和第四行以此类推,所以我们只需要枚举第一行。