枚举-OpenJudge-1222-熄灯问题

来源:互联网 发布:星光模拟器打不开软件 编辑:程序博客网 时间:2024/05/18 02:32

1222:熄灯问题

题目描述

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

这里写图片描述

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

这里写图片描述

请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。根据上面的规则,我们知道1)第2次按下同一个按钮时,将抵消第1次按下时所产生的结果。因此,每个按钮最多只需要按下一次;2)各个按钮被按下的顺序对最终的结果没有影响;3)对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。同样,按下第1、2、3、4、5列的按钮,可以熄灭前5列的灯。

输入

5行组成,每一行包括6个数字(0或1)。相邻两个数字之间用单个空格隔开。0表示灯的初始状态是熄灭的,1表示灯的初始状态是点亮的。

输出

5行组成,每一行包括6个数字(0或1)。相邻两个数字之间用单个空格隔开。其中的1表示需要把对应的按钮按下,0则表示不需要按对应的按钮。

样例输入

0 1 1 0 1 0 
1 0 0 1 1 1 
0 0 1 0 0 1 
1 0 0 1 0 1 
0 1 1 1 0 0

样例输出

1 0 1 0 0 1 
1 1 0 1 0 1 
0 0 1 0 1 1 
1 0 0 1 0 0 
0 1 0 0 0 0
解题思路
1、阅题后通过提示信息可知当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。②第2次按下同一个按钮时,将抵消第1次按下时所产生的结果。因此,每个按钮最多只需要按下一次。③各个按钮被按下的顺序对最终的结果没有影响。④对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。同样,按下第1、2、3、4、5列的按钮,可以熄灭前5列的灯。
2、我们发现灯只有按下与不按下两种情况。若我们要知道哪些灯需要按下哪些灯不需要按可以实现灯全部熄灭
这样测试最大可以达到2^30次,这样的测试无疑是费时的,我们应选择采用更合理的方式来减少测试量。
3、通过题干中提示④我们可以发现,每当有一行需要按下的灯确定后他下一行需要按下的灯也随之可以被确定,由此我们可以得出,每当第一行的灯被确定后,所有灯将均会被确定。这样我们便将原本需要的测试量2^30减小为2^6。
4、接下来我们应该思考用什么来表示这些灯的灭与亮?首先我们可以想到的是用一个二维数组进行存储,但这里我较为推荐的是用char类型的一维数组来存储,因为用char类型的一维数组不仅在空间上,而且在运行时间上,到比使用二维数据更优。每一个char类型的字符均是由8位二进制数组成的,这样我们便可以使用位运算来改变其0和1的值。

5、这里就涉及到如何将这些01串转入char类型的数组中,我们应该设计一个函数。

char oriLights[6] = { 0 };           //oriLights[]应设置在main函数外面,方便使用 for (int i = 0; i < 5; i++)              {for (int j = 5; j >= 0; j--){int s;cin >> s;Set(&oriLights[i], j, s);}}void Set(char *c, int i, int s)           {*c |= (s << i);}
如此便可以将每一行的01串写入每一个char类型的字符中。
6、接着需要遍历第一行按下与不按下的所有情况(2^6),这时我们一个新建一个char类型的数组存储oriLights[]里面的所有值。(若不采取这一种方式,在执行完第一次判断后oriLights[]里面的值就会被改变,无法进行重新判断)
char result[6] = {0};                            //结果,写下灯是否按下 char G[6] = {0}; //复制oriLights的数组 for(char temp = 0; temp < 64; temp++)             {for(int i = 0; i < 6; i++){G[i] = oriLights[i];result[i] = 0;}result[0] = temp;} 
7、再来便可以依照提示④的方式对新数组进行更改。
8、在执行完第五行后,遍历整个新数组,检查是否值全为0,若是则打印出按下方式后return,若不是则返回循环重新判断。
下面是源代码:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;char oriLights[6] = { 0 };//oriLights[]应设置在main函数外面,方便使用char result[6] = {0}; //结果,写下灯是否按下 char G[6] = {0};//复制oriLights的数组 void Set(char *c, int i, int s)//录入每一个位的灯灭情况{*c |= (s << i);}void switchs(char *c)//对测试下灯灭情况进行修改 {for(int i = 5; i >= 0; i--){char temp = (1 << i);char Temp = temp & *c;if(Temp > 0){G[0] ^=Temp;G[1] ^=Temp;if(i == 5){G[0] ^= (Temp >> 1);}else if(i == 0){G[0] ^= (Temp << 1);}else{G[0] ^= (Temp >> 1);G[0] ^= (Temp << 1);}}}}void change(int i, int j) //按提示4修改灯灭情况 {char temp = G[i-1] & (1 << j);result[i] |= temp;if(temp > 0){G[i-1] ^= temp;G[i+1] ^= temp;G[i] ^= temp;if(j == 5){G[i] ^= (temp >> 1);}else if(j == 0){G[i] ^= (temp << 1);}else{G[i] ^= (temp >> 1);G[i] ^= (temp << 1);}}}void input(char *c)//输出结果 {for(int i = 5; i >= 0; i--){char temp = *c & (1 << i);if(temp > 0){cout << "1 ";}else{cout << "0 ";}}}int main(){for(int i = 0; i < 5; i++){oriLights[i] = 0;}for (int i = 0; i < 5; i++){for (int j = 5; j >= 0; j--){int s;cin >> s;Set(&oriLights[i], j, s);}}for(char temp = 0; temp < 64; temp++){for(int i = 0; i < 6; i++){G[i] = oriLights[i];result[i] = 0;}result[0] = temp;switchs(&temp);for(int i = 1; i < 5; i++){for(int j = 5; j >= 0; j--){change(i, j);}}int nums = 0;//判断最后每个字符的ASCLL码的值是否为0,若是nums+1,当nums==5时表示在该情况下灯全灭 for(int i = 0; i < 5; i++){if(G[i] == 0){nums++;}}if(nums==5){for(int i = 0; i < 5; i++){input(&result[i]);cout << endl;}}}return 0;}