考察知识点:遍历搜索空间,枚举
解题思路:
用数组元素
(1) puzzle[i][j]表示位置(i, j)上灯的初始状态:1 表示灯是被点亮的;0 表示灯是熄灭的。用数组
元素press[i][j]表示为了让全部的灯都熄灭,是否要按下位置(i, j)上的按钮:1 表示要按下;
0 表示不用按下。由于第0 行、第0 列和第7 列不属于按钮矩阵的范围,没有按钮,可以假
设这些位置上的灯总是熄灭的、按钮也不用按下。
(2) 根据熄灯规则,如果矩阵press 是寻找的答案,那么按照press 的第一行对矩阵中的按钮
操作之后,此时在矩阵的第一行上:
*如果位置(1, j)上的灯是点亮的,则要按下位置(2, j)上按钮,即press[2][j]一定取1;
*如果位置(1, j)上的灯是熄灭的,则不能按位置(2, j)上按钮,即press[2][j]一定取0。
这样依据press 的第一、二行操作矩阵中的按钮,才能保证第一行的灯全部熄灭。而对矩阵
中第三、四、五行的按钮无论进行什么样的操作,都不影响第一行各灯的状态。依此类推
(3) 函数guess(),做两件事件。(1)根据press 第一行和puzzle
数组,按照熄灯规则计算出press 其他行的值,使得矩阵第1~4 行的所有灯都熄灭。(2)判断
所计算的press 数组能否熄灭矩阵第5 行的所有灯。如果能够就返回“true”,表示找到了答
案;否则返回“false”,表示没有找到答案。
(4)函数enumate(),对press 第一行的元素press[1][1]~ press
[1][6]的各种取值情况进行枚举。在每种取值情况下,分别调用guess(),看看是否找到了答
案。如果找到了答案,就返回主函数,否则继续下一种取值情况的判断
#include <stdio.h>
int puzzle[6][8];
int press[6][8];
bool guess() //guess函数由第一行和PUZZLE数组计算其他行的press数组
{
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]+press[r-1][c]+press[r][c-1]+press[r][c+1])%2;
for(c=1; c<7; c++) //guess函数还可以用计算得到的数组判断是否能关闭第五行
if((press[5][c-1]+press[5][c]+press[5][c+1]+press[4][c])%2!=puzzle[5][c])
return (false);
return (true);
}
void enumate() //对press[1][1]~~press[1][6] 一行各列进行枚举并调用guess看是否 那个,你懂得
{
int c;
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()
{
freopen("in.txt","r",stdin);
int cases,i,r,c;
scanf("%d",&cases);
for(r=0; r<6; r++)
puzzle[r][0]=0;
for(c=1; c<7; c++)
puzzle[0][c]=0;
for(i=0; i<cases; i++)
{
for(r=1; r<6; r++)
for(c=1; c<7; c++)
scanf("%d",&puzzle[r][c]);
enumate();
printf("PUZZLE #%d\n",i+1);
for(r=1; r<6; r++)
{
for(c=1; c<7; c++)
printf("%d",press[r][c]);
printf("\n");
}
}
return 0;
}