熄灯问题

题目不在解释,想必大家都知道。那么进行分析;算法不是我创,只是解释大神的思路。我好菜啊T^T
题目分析

–第二次按下同一个按钮时,将抵消第一次按下所产生的效果。
–每个按钮最多只需按一次。
–每个按钮按下的顺序对最后的结果没有影响。
–对第一行每个点亮的灯,只需按下第二行相应的灯,就可以熄
灭第一行所有的灯,那么如此重复下去,第1,2,3,4行的灯都可以熄灭。

解题分析

–首先我们可以枚举所有可能的按钮的开关状态,对每种情况的状态的灯
进行计算,观察是否都熄灭。
但是一共有30个开关,那么就有2^30种情况,会超时。
那么就要减少枚举的状态的数目?
那么就要看是否某个局部可以代替全部,意思是是否存在一个局部,剩余的状态只是只能是确定的一种;
经过观察(可能大神们的眼睛不一样),第一行就是一个这样的局部,因为第一行确定之后,第二行随之确定,
(想要全部熄灭第一行那么第二行就只能按下确定的几个按钮),第三行以及其后就不会再影响第一行了。

枚举方法

枚举第一行的方法有两种:
第一种:暴力循环法,六个for循环,大家都懂得。
第二种:二进制++法,我们可以把第一行看成一个六位的二进制,每次枚举++即可。

实现方法

–用设一个6*8的数组puzzle表示矩阵,在设一个press表示开关状态。
–第0行,第0列,第7列都是0。
–给press的第一行取值,并计算其他行的值

press[r+1][c]=(press[r][c-1]+press[r][c]+press[r][c+1]+puzzle[r][c])//这个公式是怎么来的,
//想要判断某个位置要不要按下,就要判断上一个的灯是不是开着,如 puzzle[i] [j],决定它
//最后状态的相关按钮为 press[i] [j-1] (左),press[i][j](它本身),press[i] [j+1](右),
//press[i-1][j](上),还有它最初的状态 puzzle[i] [j]。考虑到按两次按钮作用会抵消,需要取它们的和与2的余数。
press[5][c]+press[5][c+1]+press[5][c-1]+press[4][c]!=puzzle[5][c]//判断第五行是否熄灭,
//这是由上一个式子推导而来,一开始还在想为什么不直接判断puzzle最后一行是否都为0,后来反应过来
//细看,程序的操作是作用在press数组上,就是puzzle数组不会改变,所以最开始状态的puzzle数组和变化后的press的数组;
代码
#include<stdio.h>
int puzzle[6][8],press[6][8];

bool guess(){
	int c,r;
	for(r=1;r<5;++r){
		for(c=1;c<7;++c){
			press[r+1][c]=(puzzle[r][c]+press[r][c-1]+press[r][c]+press[r][c+1]+press[r-1][c])%2;
		}//根据press第一行和puzzle数组计算press的其他值
	}
		for(c=1;c<7;++c){
			if((press[5][c-1]+press[5][c]+press[5][c+1]+press[4][c])%2!=puzzle[5][c])
			return false;
		}// 
		return true;
}

void zhenghe(){//将数据进行整合
	int c;
	bool success;
	for(c=1;c<7;c++){
		press[1][c]=0;
	}
	while(guess()==false){//当没有正确答案时,继续下去。找到正确的答案为止
		press[1][1]++;
		c=1;
		while(press[1][c]>1){
			press[1][c]=0;
			c++;
			press[1][c]++;//模拟二进制,六位二进制
		}
	}
	return;
}
int main(){
	int n,i,r,c,a=1;
	scanf("%d",&n);
	for(r=0;r<6;++r){
		press[r][0]=0;
		press[r][7]=0;
	}
	for(c=1;c<7;++c){
		press[0][c]=0;
	}
	for(i=0;i<n;++i){
		for(r=1;r<6;++r){
			for(c=1;c<7;++c){
				scanf("%d",&puzzle[r][c]);//输入
			}
		}
	zhenghe();
	printf("PUZZLE #%d\n",a);a++;
	for(r=1;r<6;++r){
			for(c=1;c<7;++c){
				printf("%d ",press[r][c]);//输出
			}
			printf("\n");
		}
	}
	return 0;	
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值