题目描述:
有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。
所以在5x6的矩阵中,左边矩阵中用X标记的按钮表示被按下,右边的矩阵表示灯状态的改变。对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至每一盏等都熄灭。与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果。在下图中,第2行第3、5列的按钮都被按下,因此第2行、第4列的灯的状态就不改变。
请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。根据上面的规则,我们知道
1)第2次按下同一个按钮时,将抵消第1次按下时所产生的结果。因此,每个按钮最多只需要按下一次;
2)各个按钮被按下的顺序对最终的结果没有影响;
3)对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。同样,按下第1、2、3、4、5列的按钮,可以熄灭前5列的灯。
要求样例出入,输出所需的操作。
解题思路:
为了方便,给每个灯的位置定一个坐标,得到一个5x6的数组,但是为了避免第一行,第一列最后一列需要额外的操作,我们讲数组设定为6x8的二维数组。
puzzle[i][j]表示第i行第j列上灯的初试状态,1为亮,0为灭;
press[i][j]表示要不要按下ij位置的灯,1为按下;
如果这样的话有2的30次方种情况,太复杂,对算法进行优化,我们发现了规律
如果位置(1,j)的灯亮,则press[2][j]的值必为1;反之亦然,所有通过操作,将第一行的灯全部熄灭,而3,4,5行不受影响,继续后面的操作。
代码如下:
1 #include "stdio.h" 2 3 int puzzle[6][8]; 4 int press[6][8]; 5 6 bool guess(){ 7 int i,j; 8 for(i=2;i<6;i++){ 9 for(j=1;j<7;j++){ 10 press[i][j]=(press[i-1][j]+puzzle[i-1][j]+press[i-1][j-1]+press[i-2][j]+press[i-1][j+1])%2; 11 } 12 } 13 for(j=1;j<=6;j++){ 14 if(press[5][j]!=(puzzle[5][j]+press[5][j-1]+press[5][j+1]+press[4][j])%2) 15 return false; 16 } 17 return true; 18 } 19 void process(){ 20 int c; 21 for(c=1;c<7;c++) 22 press[1][c]=0; 23 while(!guess()){ 24 press[1][1]++; 25 c=1; 26 while(press[1][c]>1){ 27 press[1][c]=0; 28 c++; 29 press[1][c]++; 30 } 31 } 32 } 33 34 int main(){ 35 int i=0,j=0; 36 for(i=0;i<6;i++) 37 puzzle[i][0]=puzzle[i][7]=press[i][0]=press[i][7]=0; 38 for(j=0;j<8;j++) 39 puzzle[0][j]=puzzle[5][j]=press[0][j]=press[5][j]=0; 40 41 for(i=1;i<6;i++) 42 for(j=1;j<7;j++) 43 scanf("%d",&puzzle[i][j]); 44 45 process(); 46 printf("press is:\n"); 47 48 for(i=1;i<6;i++){ 49 for(j=1;j<7;j++){ 50 printf("%d ",press[i][j]); 51 } 52 printf("\n"); 53 } 54 }
源码下载(百度云):链接: http://pan.baidu.com/s/1ntOqG7R 密码: ewij