文章作者:Tyan
博客:noahsnail.com | CSDN | 简书
1. 枚举
枚举是基于逐个尝试答案的一种问题求解策略。
2. 熄灯问题(POJ1222)
问题描述
有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。
如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。
与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果。对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至每一盏等都熄灭。输入
5行组成,每一行包括6个数字(0或1)。相邻两个数字之间用单个空格隔开。0表示灯的初始状态是熄灭的,1表示灯的初始状态是点亮的。输出
5行组成,每一行包括6个数字(0或1)。相邻两个数字之间用单个空格隔开。其中的1表示需要把对应的按钮按下,0则表示不需要按对应的按钮。输入样例
1
2
3
4
50 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
2
3
4
51 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分析
假设当前灯亮,按钮按一次,灯变灭,再按一次,灯又变亮,恢复到了初始状态,因此,按钮按两次是没意义的。结论:按钮按偶数次没意义,按钮按奇数次与按一次一样,因此,每个按钮最多按一次。解题思路
- 枚举所有可能的按钮状态,每种状态计算一下最后的情况,看是否都熄灭。所有状态数为$2^{30}$,因此这种方案不可行。
- 如果存在某个局部,一旦这个局部的状态确定,那么剩下的其它状态只能是确定的一种,或不多的n种,则只需要枚举这个局部即可。以第一行为例,假设它就是那个局部,如果第一行的状态确定了,是不是第二行的状态就确定了呢?答案是是的,因为第一行按钮按过之后,亮的灯只有按第二行才能将其熄灭。同理,第二行按钮按下后,只能通过第三行按钮来控制灯熄灭。
- 枚举第一行的所有可能状态,每个位置有0和1两种状态,共6个位置,因此第一行的所有可能状态为$2^6=64$种,枚举状态可以通过递归实现。如果使用每个比特位代表一个灯的话,则可能的状态为数字0-63。
- 方法一
1 | #!/usr/bin/env python |
- 结果
1 | PUZZLE #1 |
- 方法二
1 | #!/usr/bin/env python |
- 结果
1 | PUZZLE #1 |
总结:这个问题比较复杂,其中隐含的一点就是局部状态确定后,后面的状态都会被确定,此时需要枚举局部状态。方法一与方法二的求解思路是一样,但实现方式不一样,方法一使用Numpy来处理数据,而方法二使用比特来处理数据。
源码地址:Numpy方法,二进制比特方法,记得给个star。