用计算机解决问题的一大好处就是可以重复枚举去试结果,这对于没有数学公式或者难以找到数学公式的题目很有用。
在枚举的时候我们要考虑能否缩小枚举的范围,这对算法运行非常有帮助。
熄灯问题:
# include <memory>
# include <string>
# include <cstring>
# include <iostream>
using namespace std;
int GetBit(char c, int i) {
// 取c的第i位
return (c >> i) & 1;
}
void SetBit(char& c, int i, int v) {
// 设置c的第i位为v
if (v)
c |= (1 << i);
else
c &= ~(1 << i);
}
void Flip(char& c, int i) {
// 将c的第i位取反
c ^= (1 << i);
}
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() {
// 初始灯的状态
char oriLights[5];
// 过程中灯的状态
char lights[5];
// 结果
char result[5];
char switchs;
// 要解决的问题个数
int T;
cin >> T;
for (int t = 1; t <= T; ++t) {
// 初始化,全置0
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) {
// 遍历首行开关的64种状态
memcpy(lights, oriLights, sizeof(oriLights));
switchs = n; // 第i行的开关状态
for (int i = 0; i < 5; ++i) {
result[i] = switchs; // 第i行的开关方案
//根据开关方案设置灯的情况
for (int j = 0; j < 6; ++j) {
// 如果开关方案显示要改变
if (GetBit(switchs, j)) {
if (j > 0) //不是第一列
Flip(lights[i], j - 1);// 改左灯
Flip(lights[i], j); // 改开关位置的灯
if (j < 5) // 不是最后一列
Flip(lights[i], j + 1); // 改右灯
}
}
if (i < 4) // 不是最后一行
lights[i + 1] ^= switchs; // 改下一行的灯
switchs = lights[i]; // 第i+1行开关方案和第i行灯情况同
}
// 如果这样操作完之后,最后一行灯全灭了
if (lights[4] == 0) {
// 输出相应的开关操作
OutputResult(t, result);
break;
}
}
}
return 0;
}
一个样例: