百练 2811:熄灯问题(枚举)

描述

有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。
在这里插入图片描述

在上图中,左边矩阵中用X标记的按钮表示被按下,右边的矩阵表示灯状态的改变。对矩阵中的每盏灯设置一个初始状态。请你按按钮,直至每一盏等都熄灭。与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一次操作的结果。在下图中,第2行第3、5列的按钮都被按下,因此第2行、第4列的灯的状态就不改变。
在这里插入图片描述

请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。根据上面的规则,我们知道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

思路:

这很明显是一个枚举的题目,但是如果你一个个的枚举,显然2^30次,时间会超时,所以我们需要找方法来减少枚举的次数。

那我们通过题目可以知道对第1行中每盏点亮的灯,按下第2行对应的按钮,就可以熄灭第1行的全部灯。如此重复下去,可以熄灭第1、2、3、4行的全部灯

通过这句话我们可以知道,根据第1行灯的开关情况可以得到第二行的按压状态,以此类推我们最后可以得到2-5行的按压状态。也就是说,由第一行的灯的开关情况,我们可以得到2-5行的按压状态。与此同时,第一行的按压状态又会对第一行的灯的开关情况产生影响。因为灯的初始状态不变,所以,最后影响2-5行的按压状态的因素就变成了第一行的按压状态。也就是说我们只需要找到第一行正确的按压状态便可以得到整个按压矩阵。此时枚举次数便降至2^6, 时间复杂度大大减小。

步骤:
  1. 确定一种第一行的按压状态(可以根据二进制加法来枚举)
  2. 根据第一行的按压状态和第一行的灯的开关情况,依次求出2-5行的按压状态
  3. 确定此情况下第五行的灯是否完全熄灭(因为由题目知,此时1-4应当全灭)
  4. 若第五行全灭,则此时的按压矩阵即为答案,否则接着重复上述过程,直到找到答案

话不多说(虽然有点多),,,上代码!!!

#include <stdio.h>
int button[10][10];/*记录按钮状态*/
int mp[10][10];/*记录灯的初始状态*/ 

bool guess()
{
	for(int i=2;i<=5;i++)//确认2-5行的按压状态 
	{
		for(int j=1;j<=6;j++)
		{
			button[i][j]=(mp[i-1][j]+button[i-1][j]+
			button[i-2][j]+button[i-1][j-1]+button[i-1][j+1])%2;
			//下一行的按压状态=(上一行的灯的状态+按压状态+灯的上、左、右的按压状态)%2;
			//%2是因为存在按压两次相互抵消 ,且考虑到灯的状态因别处的按压而改变的情况 
			//这里如果不明白,可以手推一下(万能手写) 
		}
	} 
	for(int i=1;i<=6;i++)//若按压矩阵正确,则第五行的按压状态应当与第五行灯的原始状态一样 
	{                   //因为只有一样,才能确保第五行的灯熄灭 
		if(button[5][i]!=(mp[5][i]+button[5][i-1]+button[4][i]+button[5][i+1])%2)
		return false;
	}
	return true;
}

void look()
{
	//for(int i=1;i<=6;i++) button[1][i]=0;/*令第一行的初始状态全为0*/没有也可以过(因为全局变量) 
	//这里用的是模拟二进制加法来表示第一行的变化,并根据此变化来进行枚举 
	int pos;
	while(guess()==false)
	{
		button[1][1]++;//从左边加,向右进位 
		pos=1;
		while(button[1][pos]>1)//这里如果不懂可以手写一下,弱弱我就经常这么干 
		{
			button[1][pos]=0;
			pos++;
			button[1][pos]++;
		} 
	} 
} 

int main( )
{
//	for(int i=0;i<8;i++) button[0][i]=0;  这里是给矩阵赋初值的,没有也能过, 
//	for(int i=1;i<6;i++) button[i][0]=0,button[i][7]=0;应该是因为全局变量默认为0吧
	for(int i=1;i<=5;i++)
	{
		for(int j=1;j<=6;j++)
		{
			scanf("%d",&mp[i][j]);
		} 
	}
	look();/*寻找答案矩阵*/
	for(int i=1;i<=5;i++)
	{
		for(int j=1;j<=6;j++)
		{
			if(j!=6) printf("%d ",button[i][j]);
			else printf("%d\n",button[i][j]);
		}
	}
	return 0;
}

最后,如果你还不明白,没关系,弱弱我再赠送大佬网址一个:
[http://blog.sina.com.cn/s/blog_7393daaf0100svoo.html]

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值