枚举算法--熄灯问题

最近在学习一些基础的算法,编程这种事情,只能慢慢摸索,自己多去锻炼才会取得好的效果!!

熄灯问题

问题:对于给定的一个按钮矩阵5*6,使得改变每个元素的状态之后(同时改变一个状态会使得它的周围的状态都会改变)全部的灯都为熄灭状态。(其中0代表是熄灭的,1代表是亮着的)

基本思路:

1,如果直接开始采用枚举法,则共有2^5*6要列举的,不妨选取一个局部状态,如果确定了该局部状态,后面的状态会基本定下来,首先对第一列的元素进行改变,若要使第一列的灯全部熄灭,第二列只有唯一一种解法,同时,若使第二列全部熄灭,第三列也是唯一确定的……以此类推,最后一列的状态改变使得倒数第二列全部熄灭,同时最后一列也全部熄灭,则由于第一列产生的改变是正确的,否则,重新选取一个局部状态。所以只需枚举局部状态,就可以完成了,此时有2^5需要列举。

2,在一个矩阵中,有三种情况,在角上(处理三盏灯)、边上(处理四盏灯)、内部(处理五盏灯)时,当改变他们的状态时对周边所产生的改变是不一样的,不妨在矩阵外面罩上一层,使得全部满足在内部的情况,此时,矩阵为6*8。多取的行列只需要设置为0就可以了。

3,枚举方式:将按钮矩阵的第一行看做一个二进制数,通过实现++操作实现。(从左往右)

4,选择两个数组,一个放置初始情况,另一表则表示需要进行的操作(0代表不改变,1代表改变状态)。分别为puzzle[6][8],press[6][8]。

          对于给定的press的第一行取值,计算出press的其它行的值:

                     press[r+1][c]=(puzzle[r][c]+press[r][c-1]+press[r][c]+press[r][c+1]+press[r-1][c])%2,0<r<5,0<c<7;

5,程序实现代码:

#include<iostream>
#include<stdlib.h>
using namespace std;

int puzzle[6][8];//初始值的按钮矩阵
int press[6][8];//设置的按钮矩阵
//验证由局部状态引起的变换是不是满足条件的
bool guess(){
	int r, c;
	for (r = 1; r < 5; ++r)
		for (c = 1; c < 7; ++c)
			press[r + 1][c] = (puzzle[r][c] + press[r][c] + press[r - 1][c] + press[r][c - 1] + press[r][c + 1]) % 2;
	//确定了第一行之后,剩下的行数(不包括最后一行)的状态是由puzzle数组和press数组共同掌握的。
	//上一行的亮暗是由下一行决定的(相同的列),但是上一行的状态又是由上、左、右以及自己的值、是否被按下所决定的。因此上一行最终若为0.则不改变,否则改变
	for (c = 1; c < 7; ++c)//判断第五行的灯能否被其余的press数组熄灭
		if ((press[5][c] + press[5][c + 1] + press[5][c - 1] + press[4][c]) % 2 != puzzle[5][c])
			//如果改变四个的状态所得的结果与初始状态一样,则表明已经熄灭了所有的灯;若为1时,改变奇数个状态,则表明为使得灯熄灭,此时不满足条件1!=1,返回true;
			//改变偶数个状态,则表明原来的灯不变,还是亮着的,返回false,继续下一个局部状态再进行验证;若为0则改变状况刚好相反!
			return false;
	return true;//在这个前面加了一个else导致出错!!
}
void enumerate(){
	int c;
	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 r, c, cases, i;
	cout << "input cases : ";
	cin >> cases;
	for (r = 0; r < 6; ++r){
		press[r][0] = press[r][7] = 0;
		puzzle[r][0] = puzzle[r][7] = 0;
	}

	for (c = 1; c < 7; ++c){
		press[0][c] = 0;
		puzzle[0][c] = 0;
	}

	//输入数据
	for (i = 1; i <= cases; ++i){
		for (r = 1; r < 6; ++r){
			for (c = 1; c < 7; ++c)
				cin >> puzzle[r][c];
		}
		//}
		enumerate();
		//输出数据
		//for (i = 0; i < cases; ++i){
		cout << "puzzle #" << i << endl;
		for (r = 1; r < 6; ++r){
			for (c = 1; c < 7; ++c)
				cout << press[r][c];
			cout << endl;
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值