当先锋百科网

首页 1 2 3 4 5 6 7

枚举法解熄灯问题

北大郭炜老师:程序与算法(二)

题目描述

有一个由按钮组成的矩阵,其中每行有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