枚举法解熄灯问题
北大郭炜老师:程序与算法(二)
题目描述
有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。
请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。根据上面的规则,我们知道
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
解题流程
看到这道题后,想起了刚学c语言时八皇后的问题,然后就对这道题产生了兴趣。
解题规则郭炜老师在一开始就说了
1.每个按钮按一次即可
2.顺序对结果无影响
3.对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯。同样,按下第1、2、3、4、5列的按钮,可以熄灭前5列的灯。
解题的关键就在于第三点
这道题如果我每种情况都枚举出来的话,一共是232种,显然是不合理的,而第三种规则告诉了我们这里只用枚举第一行26种情况即可。
我们用两列灯来举个简单的例子
假如为
0 0 1 1 0 1
0 1 1 0 1 1
首先枚举第一种情况,即按下第一行第一列的灯,然后就变成了:
1 1 1 1 0 1
1 0 1 0 1 1
这种情况下我要想最后第一行第一列的灯为0的话,那么我一定要按下第二行第一列的灯,即第二行的灯是否要被按下是受第一行所控制的,同理,第三行也受第二行的控制,这样依次推下去我们会发现,后面几行灯是否被按下,全部受第一行控制,所以在枚举时只需要枚举出第一行的所有情况即可。
在进行枚举的时候,为了减少时间和空间开销,我们可以将二维数组转化为一维数组
因为二维数据每个位置的值只能是0或者1
假如第一行为 0 0 1 1 0 1,那么我们新建的一维数组的第一位的值就为13,每次更改的时候采用位运算就行了,这里不做过多描述
首先写出三个函数方便待会使用
//输入的时候用到
int getbit (char c,int i)
{
return (c>>i)&1;
}
//将c的第i位设置为v
char setbit (char c,int i,int v)
{
if(v)
return c|(1<<i);
else
return c &( ~(1<<i));
}
//翻转c的第i位
char