题目不在解释,想必大家都知道。那么进行分析;算法不是我创,只是解释大神的思路。我好菜啊T^T
题目分析
–第二次按下同一个按钮时,将抵消第一次按下所产生的效果。
–每个按钮最多只需按一次。
–每个按钮按下的顺序对最后的结果没有影响。
–对第一行每个点亮的灯,只需按下第二行相应的灯,就可以熄
灭第一行所有的灯,那么如此重复下去,第1,2,3,4行的灯都可以熄灭。
解题分析
–首先我们可以枚举所有可能的按钮的开关状态,对每种情况的状态的灯
进行计算,观察是否都熄灭。
但是一共有30个开关,那么就有2^30种情况,会超时。
那么就要减少枚举的状态的数目?
那么就要看是否某个局部可以代替全部,意思是是否存在一个局部,剩余的状态只是只能是确定的一种;
经过观察(可能大神们的眼睛不一样),第一行就是一个这样的局部,因为第一行确定之后,第二行随之确定,
(想要全部熄灭第一行那么第二行就只能按下确定的几个按钮),第三行以及其后就不会再影响第一行了。
枚举方法
枚举第一行的方法有两种:
第一种:暴力循环法,六个for循环,大家都懂得。
第二种:二进制++法,我们可以把第一行看成一个六位的二进制,每次枚举++即可。
实现方法
–用设一个6*8的数组puzzle表示矩阵,在设一个press表示开关状态。
–第0行,第0列,第7列都是0。
–给press的第一行取值,并计算其他行的值
press[r+1][c]=(press[r][c-1]+press[r][c]+press[r][c+1]+puzzle[r][c])//这个公式是怎么来的,
//想要判断某个位置要不要按下,就要判断上一个的灯是不是开着,如 puzzle[i] [j],决定它
//最后状态的相关按钮为 press[i] [j-1] (左),press[i][j](它本身),press[i] [j+1](右),
//press[i-1][j](上),还有它最初的状态 puzzle[i] [j]。考虑到按两次按钮作用会抵消,需要取它们的和与2的余数。
press[5][c]+press[5][c+1]+press[5][c-1]+press[4][c]!=puzzle[5][c]//判断第五行是否熄灭,
//这是由上一个式子推导而来,一开始还在想为什么不直接判断puzzle最后一行是否都为0,后来反应过来
//细看,程序的操作是作用在press数组上,就是puzzle数组不会改变,所以最开始状态的puzzle数组和变化后的press的数组;
代码
#include<stdio.h>
int puzzle[6][8],press[6][8];
bool guess(){
int c,r;
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第一行和puzzle数组计算press的其他值
}
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 zhenghe(){//将数据进行整合
int c;
bool success;
for(c=1;c<7;c++){
press[1][c]=0;
}
while(guess()==false){//当没有正确答案时,继续下去。找到正确的答案为止
press[1][1]++;
c=1;
while(press[1][c]>1){
press[1][c]=0;
c++;
press[1][c]++;//模拟二进制,六位二进制
}
}
return;
}
int main(){
int n,i,r,c,a=1;
scanf("%d",&n);
for(r=0;r<6;++r){
press[r][0]=0;
press[r][7]=0;
}
for(c=1;c<7;++c){
press[0][c]=0;
}
for(i=0;i<n;++i){
for(r=1;r<6;++r){
for(c=1;c<7;++c){
scanf("%d",&puzzle[r][c]);//输入
}
}
zhenghe();
printf("PUZZLE #%d\n",a);a++;
for(r=1;r<6;++r){
for(c=1;c<7;++c){
printf("%d ",press[r][c]);//输出
}
printf("\n");
}
}
return 0;
}