C++之练习题5

时间:2023-02-12 21:22:35
5.有一个由按钮组成的矩阵,其中每 行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位 置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭; 如果灯原来是熄灭的,则会被点亮。
       在矩阵角上的按钮改变3盏灯的状态
       在矩阵边上的按钮改变4盏灯的状态

       其他的按钮改变5盏灯的状态

C++之练习题5

       在上图中,左边矩阵中用X标记的按钮表示被按下,右边的矩阵表示灯状态的改变,对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至 每一盏等都熄灭。与一盏灯毗邻的多个按钮被按下时,一 个操作会抵消另一次操作的结果。在下图中,第2行第3、5 列的按钮都被按下,因此第2行、第4列的灯的状态就不改变

C++之练习题5确定需要按下哪些按钮,恰好使得所有的灯都熄灭。

输入:第一行是一个正整数N,表示需要解决的案例数。每个案例由5行组成,每一行包括6 个数字。这些数字以空格隔开,可以是0或1。0 表示灯的初始状态是熄灭的,1表示灯的初始状态是点亮的。
输出:对每个案例,首先输出一行,输出字符 串“PUZZLE #m”,其中m是该案例的序号。接着按照该案例的输入格式输出5行,其中的1表示需要把对应的按钮按下,0则表示不需要按对应的按钮。每个数字以一个空格隔开。

第2次按下同一个按钮时,将抵消第1次按下时所产生的结果。因此,每个按钮最多只需要按 下一次。各个按钮被按下的顺序对最终的结果没有影响对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。

适用于一行有N个按钮的办法:
一个6位二进制数的所有取值正好是64种,让该数的每一位对应于anSwitch[0]里的一个元素(anSwitch[0][5] 对应最高位,anSwitch[0][4]对应次高位….. ),那么这个二进制数的每个取值正好表示了第一行按钮的一种状态。
0 的二进制表示形式是00 0000,即代表所有按钮都不按下
63 的二进制表示形式是11 1111,即代表所有按钮都按下
5 的二进制表示形式是00 00101,即代表右数第1,3个按钮按下
如果一行有N个按钮,那么就用一个N位二进制数

枚举过程:

要写一个从二进制数到状态的转换函数:
void SwitchStatus( int n, int * pSwitch);
该函数将整数n( 0 =<n<64)的二进制表示形式对应到数组pSwitch里去
(anSwitch[0][i] 对应第i位)
void SwitchStatus( int n, int * pSwitch){
for( i = 0;i < 6 ;i ++ )
pSwitch[i] = (n >> i ) & 1;
}

保存灯的初始状态,从第一行开始依次操作按钮使得灯 按行依次熄灭
第0行的按钮状态为枚举值
第k+1行的按钮状态应和第k行的灯状态一致
最后看最后一行灯是不是全灭
要写一个让按钮起作用的函数
void ApplySwitch( int * pLights, int * pNextLights,int * pSwitchs) ;
pSwitchs 表示一行按钮的状态
pLights 表示与按钮同一行的灯的状态
pNextLights表示按钮下一行的灯的状态
本函数根据pSwitchs 所代表的按钮状态,计算这行按钮起作用后,pLights行(当前、左、右)和pNextLights行的灯的状态
不考虑按钮的上一行的灯,是因为设定pSwitchs的值的时候, 已经确保会使得上一行的灯变成全灭(或没有上一行)

void ApplySwitch( int * pLights, int * pNextLights, int * pSwitchs) {
for( int i = 0;i < 6; i ++ ) { //依次让每个按钮起作用
if( pSwitchs[i] ) {
//按钮左边的灯改变状态
if( i > 0 )
pLights[i-1] = 1 - pLights[i-1];
//按钮所在位置的灯改变状态
pLights[i] = 1 - pLights[i];
//按钮右边的灯改变状态
if( i < 5)
pLights[i+1] = 1 - pLights[i+1];
//按钮下边的灯改变状态
pNextLights[i] = 1 - pNextLights[i];
}
}
}

程序框架:

for( int n = 0; n < 64; n ++ ) { //遍历首行按钮的64种状态
1. 用输入值初始化灯状态矩阵anPuzzle;
2. 用SwitchStatus算出n所代表的第0行按钮状态;
3. for( int k = 0; k < 5; k ++ ) {//逐行让按钮起作用,并算//出下一行按钮应该是什么状态}
    1用ApplySwitch让第k行按钮起作用,算出对第k+1 行灯的影响;
    2 根据第k行的灯状态设置第k+1行的按钮状态;
4. 看最后一行灯是不是全灭。若全部熄灭,则找到解, 不用再试下一种状态;

#include <memory.h>
#include <string.H>
#include <iostream>
using namespace std;
int T; int anPuzzle[6][6];
int anOriPuzzle[5][6];
int anSwitch[6][6]; //按钮状态
int i,j;
void OutputResult(int t) //输出结果
{
cout << "PUZZLE #" << t << endl;
for( int i = 0;i < 5; i ++ ) {
for( int j = 0; j < 6; j ++ ) {
cout << anSwitch[i][j];
if( j < 5 ) cout << " ";
}
cout << endl;
}
}

int main() {
cin >> T;
for( int t = 0; t < T; t ++ ) {
for( i = 0;i < 5; i ++ )
for( j = 0; j < 6; j ++ )
cin >> anOriPuzzle[i][j];
for( int n = 0; n < 64; n ++ ) { //遍历首行按钮的64种状态
memcpy( anPuzzle,anOriPuzzle,sizeof(anPuzzle));
//算出n所代表的按钮状态,放到anSwitch[0]
SwitchStatus( n, anSwitch[0]);
//下面逐行让按钮起作用,并算出下一行按钮应该是什么状态,再让它们起作用……
for( int k = 0; k < 5; k ++ ) {
//算出第k行按钮起作用后的结果
ApplySwitch( anPuzzle[k],
anPuzzle[k+1],anSwitch[k]);
//第k+1行的按钮状态应和第k行的灯状态一致
memcpy( anSwitch[k+1], anPuzzle[k],sizeof(anPuzzle[k]));
}

bool bOk = true; //记录最后一行灯是不是全灭
//看最后一行灯是不是全灭
for( k = 0; k < 6; k ++ ) {
if( anPuzzle[4][k] ) {
bOk = false;
break;
}
}
if( bOk ) {
OutputResult(t+1); //输出解
break; //找到解,就不用再试下一种状态
}
} // for( int n = 0; n < 64; n ++ )
}
}