欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  互联网

算法1.枚举法解决熄灯问题

程序员文章站 2022-06-28 15:21:01
枚举法解熄灯问题北大郭炜老师:程序与算法(二)题目描述有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。根据上面的规则,我们知道1)第2次按下同一个按钮时,将抵消第1次...

枚举法解熄灯问题

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

题目描述

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