枚举法解熄灯问题
北大郭炜老师:程序与算法(二)
题目描述
有一个由按钮组成的矩阵,其中每行有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 flipbit(char c,int i)
{
return c^(1<<i);
}
//输出
void outputresult(char result[])
{
printf("结果为:\n");
int i,j;
for(i=0;i<5;i++)
{
for(j=0;j<6;j++)
printf("%d ",getbit(result[i],j));
printf("\n");
}
}
完整代码如下
#include<stdio.h>
int getbit (char c,int i)
{
return (c>>i)&1;
}
//设置
char setbit (char c,int i,int v)
{
if(v)
return c|(1<<i);
else
return c &( ~(1<<i));
}
//翻转
char flipbit(char c,int i)
{
return c^(1<<i);
}
void outputresult(char result[])
{
printf("结果为:\n");
int i,j;
for(i=0;i<5;i++)
{
for(j=0;j<6;j++)
printf("%d ",getbit(result[i],j));
printf("\n");
}
}
int main()
{
int i,j,bit,n;
char light[5];//初始状态
char light_chage[5];//中间状态
char result[5];//结果
for(i=0;i<5;i++)
{
for(j=0;j<6;j++)
{
scanf("%d",&bit);
light[i]=setbit(light[i],j,bit);
}
}
for( n=0;n<64;n++)
{
for(i=0;i<5;i++)
light_chage[i]=light[i];//将初始灯的状态复制到改变量中
int switchs =n;//确定第一行行开关的状态
//开始遍历,从第一行到第五行
for(i=0;i<5;i++)
{
result[i]=switchs;//修改每一行开关的状态
for(j=0;j<6;j++)
{
if(getbit(switchs,j))
{
if(j>0)
light_chage[i]=flipbit(light_chage[i],j-1);//对j左边翻转
light_chage[i]=flipbit(light_chage[i],j);//j进行翻转
if(j<5)
light_chage[i]=flipbit(light_chage[i],j+1);//对j右边翻转
}
}
if(i<4)
light_chage[i+1]^=switchs; //确定下一行灯的状态
switchs=light_chage[i];//确定下一行开关状态
}
if(!light_chage[4])//输出答案
{
outputresult(result);
break;
}
}
return 0;
}
算法学习从今天开始把
作为一个基础差到爆的小白
希望能通过算法学习在年底的csp考试中能够取得一个不让我挂科的成绩
p大的这门算法课能帮我补一下落后的东西
刷点力扣和哈斯特oj的题
慢慢来吧
wtcl XD
本文地址:https://blog.csdn.net/cxzzzznb/article/details/109275432