本题的解题思路是:
(1) 首先对任意一初始状态,解是唯一的。
(2) 要使得第1行灯全部关闭,可以通过按下第2行相应的按钮来实现,因此依次按下2~5行的按钮,可以使得前面4行的灯全部关闭,但这时第5行可能还有些灯是开着的。所以这种方法行不通,原因是第1行的按钮没有按下。
(3) 第1行6盏灯,按下与否一共有2^6 = 64种可能。按下第1行后,为了使得第1行的灯全部关闭,第2行各按钮的按下与否就确定下来了;同样为了使得第2行的灯全部关闭,第3行各按钮的按下与否也就确定下来了;一直到第5行,其按法及各灯的状态也确定下来了。
(4) 枚举第1行6盏灯的64种按法,当某种按法使得第5行的灯全部关闭,则找到解了。因为矩阵为5×6的,规模很小,所以尽管需要枚举很多种情况,但也不会超时。
收获:
1.按位与运算符(&)
完整代码:
#include<stdio.h>
#include<string.h>
int light[5][6];
int ans[5][6];
void Anxia(int x,int y)//0表示关闭,1表示打开
{
ans[x][y] = 1;//按下的那一刻,要记录该位置的按下操作
light[x][y] = 1 - light[x][y];//当前位置取反
if(x>0) light[x-1][y] = 1 - light[x-1][y];
if(y>0) light[x][y-1] = 1 - light[x][y-1];
if(x<4) light[x+1][y] = 1 - light[x+1][y];
if(y<5) light[x][y+1] = 1 - light[x][y+1];
}
void process()
{
int temp[5][6];
int i,j;
for(i=0;i<5;i++)
for(j=0;j<6;j++)scanf("%d",&temp[i][j]);//总是忘记取地址符&
int z = 0;
for(z=0;z<64;z++)//枚举第一行的64种可能
{
memcpy(light,temp,sizeof(light));
memset(ans,0,sizeof(ans));
for(j=0;j<6;j++)//根据z求第1行各按钮是否按下
{
if(z&(1<<j))Anxia(0,j);
}
for(i=1;i<5;i++)
for(j=0;j<6;j++)
if(light[i-1][j] == 1)Anxia(i,j);
for(j=0;j<6;j++)
{
if(light[4][j] == 1)break;
}
if(j>=6)
{
for(i=0;i<5;i++)
{
printf("%d",ans[i][0]);
for(j=1;j<6;j++)printf(" %d",ans[i][j]);//输出结果是ans数组,不是light数组
printf("\n");
}
break;
}
}
}
int main()
{
int n = 0;
int i = 0;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
printf("PUZZLE #%d\n", i);
process();
}
return 0;
}