//解决熄灯问题(将所有灯都熄灭)
//输入:
/*
第一行输入一个正整数N,表示需要解决的案例数
每个案例为5*6的矩阵(灯的初始状态)
这些数字以空格隔开,0熄灭,1点亮
*/
//输出:
/*
对每个案例,首先输出一行
输出字符串“PUZZLE #m”,其中m是该案例的序号
接着按照该案例的输入格式输出5行
1表示需要把对应的按钮按下
0表示不需要按
每个数字以空格隔开
*/
//分析:
/*
每个按钮最多只需要按下一次
各个按钮按下的顺序没有影响
第一想法:枚举所有可能的按钮开关状态,
对每个状态计算一下最后灯的情况,看是否都熄灭(2的30次方,不可取)
基本思路:如果存在某个局部,一旦这个局部状态被确定,
那么剩余其他部分的状态,只能是确定的一种,或者不多的n种,
那么只需要枚举这个局部状态即可(第1行就可以当做一个局部)
为熄灭第1行的灯,第2行的开关状态是唯一的,
同理,为熄灭第2行的灯,第3行的开关状态也唯一
如果只枚举第1行:2的6次方种状态=64种状态
同理如果只枚举第1列:2的5次方种状态=32种状态
枚举方法:
1.六重for循环
2.将每一行看做二进制数
3.给定press第1行的取值,计算press的其他行的值
核心代码(知道第1行,计算其下方灯的按不按状态):
press[r+1][c]=(puzzle[r][c]+press[r][c-1]+press[r][c]+press[r][c+1]+press[r-1][c])%2;
上述分析:
假设我们要求(2,2)按或不按,要判断(1,2)状态及其其他周边按钮是否按下,
进而判断(2,2)需不需要按下;
eg:(1,2)的状态+(1,1)按否+(1,2)安否+(1,3)按否+(0,2)按否
按了或者亮的状态为1,否则为0;将这些结果相加后,
若为偶数,则处于熄灭状态,(2,2)不需要按下
若为奇数,则处于点亮状态,(2,2)需要按下
(1,1)到(5,6)
*/
#include<iostream>
using namespace std;
bool guess(int puzzle[6][8],int press[6][8]){
//判断当前枚举的按扭状态,是否满足将所有灯都熄灭的状态
//传入的press数组只枚举了第1行所以接下来要计算其他行的情况
//再进行判断能否将灯全部熄灭
/*
用6*8按钮矩阵来简化下一行按钮值的计算公式
根据已知的状态puzzle数组和按钮press[1][]
用公式(press[r+1][c]=(puzzle[r][c]+press[r][c-1]+press[r][c]+press[r][c+1]+press[r-1][c])%2;)
计算使得1-4行所有灯熄灭的press其他行的值,
再判断所计算的press数组能否熄灭矩阵第5行的所有灯(若第5行所有灯都能被熄灭,那即可全部熄灭)
*/
int r,c;
//根据给定的press按钮按下情况第1行和puzzle数组,计算press其他行的值
for(r=1;r<5;r++)
{
for(c=1;c<7;c++)
{
press[r+1][c]=(puzzle[r][c]+press[r][c-1]+press[r][c]+press[r][c+1]+press[r-1][c])%2;
}
}
//判断计算得到的press数组能否熄灭第5行的所有灯
for(c=1;c<7;c++)
{
if((press[5][c-1]+press[5][c]+press[5][c+1]+press[4][c])%2!=puzzle[5][c])
return false;
}
return true;
}
void enumerate(int puzzle[6][8],int press[6][8]){
//用来枚举press第1行元素的状态,用二进制数累加
/*
press[1][]中每一个元素表示一个二进制数位0/1,
左边低位,右边高位
通过模拟二进制加法方式实现枚举(需要进位)
*/
int c;
bool success;
//初始化
for(c=1;c<7;c++)
{
press[1][c]=0;
}
while(guess(puzzle,press)==false){
//枚举第一行
press[1][1]++;
c=1;
//模拟二进制数进位操作(左边低位,右边高位00-->10-->11-->001)
while(press[1][c]>1){
press[1][c]=0;
c++;
press[1][c]++;
}
}
return;
}
int main()
{
int cases,i,r,c;
cout<<"请输入案例数:";
cin>>cases;
int puzzle[6][8],press[6][8];
//初始化多余的左右两列
for(r=0;r<6;r++)
press[r][0]=press[r][7]=0;
//初始化多余的最上面一行
for(c=1;c<7;c++)
press[0][c]=0;
for(i=0;i<cases;i++){
//第i+1个案例
cout<<"请输入第"<<i+1<<"个案例:"<<endl;
//输入值,初始化灯的状态
for(r=1;r<6;r++){
for(c=1;c<7;c++){
cin>>puzzle[r][c];
}
}
enumerate(puzzle,press);
cout<<"案例"<<i+1<<"结果为:"<<endl;
for(r=1;r<6;r++){
for(c=1;c<7;c++){
cout<<press[r][c];
}
cout<<endl;
}
}
return 0;
}