问题描述:
有一个由按钮组成的矩阵, 其中每行有6个按钮, 共5行,每个按钮的位置上有一盏灯
当按下一个按钮后, 该按钮以及周围位置(上边, 下边, 左 边, 右边)的灯都会改变一次
如果灯原来是点亮的, 就会被熄灭
如果灯原来是熄灭的, 则会被点亮
输入:
输入一个案例,案例由5行组成, 每一行包括6个数字,这些数字以空格隔开, 可以是0或1。0 表示灯的初始状态是熄灭的,1 表示灯的初始状态是点亮的。
输出:
按照该案例的输入格式输出5行 ,1 表示需要把对应的按钮按下,0 表示不需要按对应的按钮,每个数字以一个空格隔开 。
示例1:
样例输入:
样例输出:
示例2:
样例输入:
样例输出:
分析:
对问题的分析:
本题要求我们通过按按钮来熄灭灯,虽然按灯的次数没有限制,但只有最多按一次是有意义的(因为第二次按下同一按钮,将会抵消第一次按下所产生的结果,等价于不按),因此,
有意义的是 按一次 或者 不按。其次,按钮按下的顺序是否有影响,有当前状态有影响,对最终结果无影响,稍后解释。
经过对题目的分析,可以发现,方案数只有有限多种情况,即解空间是有限的,因而可以考虑采用枚举算法,那么,枚举所有按钮情况是否可行呢?
——一个按钮有两种状态(按下或不按下),一共有5*6 = 30个开关,对应状态数为2^30,显然非常耗时,不可取。
既然解空间是一定的,我们有没有办法缩小解空间呢?
基本思路:
如果存在一个局部状态,一旦这个局部状态给定了,那么剩余状态只能是确定的若干种,如此,我们只需枚举这个局部的状态。
事实上,这样的局部状态是确实存在的,比如第一行,当第一行的状态确定后,由于我们的目的是将所有灯都熄灭,对于第一行中未熄灭的灯,只有通过第二行才能将其熄灭,同理,第二行、第三行...
第五行都是如此,最后,根据第五行的状态来判定是否所有灯都已熄灭(因为前四行都由其后一行保证均熄灭,只有第五行无法保证),如果成立,则得到了一个有效解,否则继续枚举下一组情况。
在这个方法中,我们只枚举第一行(事实上第一列也是可行的,且效率更高),状态数仅为2^6=64种,在效率上足以令人满意。
实现方法:
首先建立两个二维矩阵,用于存储puzzle(灯的初始状态)和press(按钮的状态),为了简化矩阵左右上下边界的代码量以及index与序号对应,将矩阵从5*6扩充为6*8,并初始化为0。
接着,我们需要枚举第一行的所有情况,传统的办法是写6个for循环,但这显然不是个好办法(比如矩阵是100列),观察发现,矩阵中每一个元素的取值只能是0/1,对第一行整体观察,其取值范围为
从000 000,到111 111(二进制),对应于十进制的0到63,因此,我们可以枚举一个整型变量,它从0自增到63,然后用位运算分解赋到相应的位置。
对于枚举的每一种情况,都要判断该情况是否可行,具体方法是,由于第一行的状态已经由分解给定,逐行分析第2行到第5行,最后判断第5行的puzzle是否为全0,如果是则输出press并返回,
否则继续枚举下一组情况,如果枚举了全部情况但不存在解,则输出“无解”。
代码(c++):
1 #include <iostream> 2 #include <string.h> 3 using namespace std; 4 int puzzle[6][8] = {0}; 5 int puzzle_copy[6][8]; 6 int press[6][8] = {0}; 7 void Initialize(); //对puzzle初始化 8 void Breakdown(int); //分解整型变量,并赋给相应位置 9 bool Guess(); //测试当前分解情况是否可行,可行则返回true,否则返回false 10 void Execute(int, int); //执行第i行第j列按下操作 11 void Print(); //打印Press矩阵 12 int main() 13 { 14 Initialize(); 15 int i; 16 for (i = 0; i < 64; ++i) { 17 memcpy(puzzle, puzzle_copy, sizeof(puzzle)); 18 Breakdown(i); 19 if (Guess()) { 20 Print(); 21 break; 22 } 23 } 24 if (i == 64) 25 cout << "无解\n" << endl; 26 return 0; 27 } 28 29 void Initialize() 30 { 31 //用户输入5*6矩阵 32 freopen("input.txt", "r", stdin); 33 for (int i = 1; i < 6; ++i) 34 for (int j = 1; j < 7; ++j) 35 cin >> puzzle[i][j]; 36 memcpy(puzzle_copy, puzzle, sizeof(puzzle)); 37 } 38 39 void Breakdown(int x) 40 { 41 int i = 6; 42 while (x > 0) { 43 press[1][i] = x & 1; 44 Execute(1, i--); 45 x >>= 1; 46 } 47 } 48 49 bool Guess() 50 { 51 for (int i = 2; i < 6; ++i) { 52 for (int j = 1; j < 7; ++j) { 53 if (puzzle[i - 1][j] == 1) { 54 press[i][j] = 1; 55 Execute(i, j); 56 } 57 else 58 press[i][j] = 0; 59 } 60 } 61 for (int i = 1; i < 7; ++i) { 62 if (puzzle[5][i] == 1) 63 return false; 64 } 65 return true; 66 } 67 68 void Execute(int i, int j) 69 { 70 if (press[i][j] == 1) { 71 puzzle[i][j] ^= 1; //取反 72 puzzle[i - 1][j] ^= 1; 73 puzzle[i][j - 1] ^= 1; 74 puzzle[i][j + 1] ^= 1; 75 puzzle[i + 1][j] ^= 1; 76 } 77 } 78 79 void Print() 80 { 81 cout << "press结果为:" << endl; 82 for (int i = 1; i < 6; ++i) { 83 for (int j = 1; j < 7; ++j) { 84 cout << press[i][j] << ‘ ‘; 85 } 86 cout << endl; 87 } 88 }
注意:这里的输入流重定向到了input.txt文件,为了调试的方便。
原文地址:https://www.cnblogs.com/laideng/p/11433017.html