熄灯问题——枚举

1 问题描述

有一个由按钮组成的矩阵,其中每行有6个按钮,共5行。每个按钮的位置上有一盏灯。当按下一个按钮后,该按钮以及周围位置(上边、下边、左边、右边)的灯都会改变一次。即,如果灯原来是点亮的,就会被熄灭;如果灯原来是熄灭的,则会被点亮。在矩阵角上的按钮改变3盏灯的状态;在矩阵边上的按钮改变4盏灯的状态;其他的按钮改变5盏灯的状态。请你写一个程序,确定需要按下哪些按钮,恰好使得所有的灯都熄灭。

2 问题分析

我们可以用对第一行进行枚举的方法,以此确定第一行的状态,这样,其余行也就相应确定了。此外,我们应该注意:

① 第二次按下同一个按钮将取消第一次的结果,所以一个按钮最多只需按一次;

② 按钮按下的顺序对结果不会产生影响

我们可以将每一盏灯看做一个比特,这样每一行我们可以用6个比特来存,因此,我们只需利用一个一维的字符数组就能存下整个矩阵。(一个元素拥有8个比特)

这里,我们通过位运算来操作每一个字符中的比特。

3 测试样例

样例输入

2

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

0 0 1 0 1 0

1 0 1 0 1 1

0 0 1 0 1 1

1 0 1 1 0 0

0 1 0 1 0 0


样例输出:

PUZZLE #1

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

PUZZLE #2

1 0 0 1 1 1

1 1 0 0 0 0

0 0 0 1 0 0

1 1 0 1 0 1

1 0 1 1 0 1 


4 完整代码

#include<iostream>
#include<memory>
#include<string>
#include<cstring>
using namespace std;

char initLight[5];//原始灯的矩阵 
char lights[5];//变化中的灯的矩阵
char result[5];//结果 
//对灯的操作实际上就是操作一个字符中的一个比特 
int getBit(char c, int i)//取字符c的第i个比特 
{
	return (c >> i) & 1;
	//即把字符c的第i个比特放到最低位,再与1进行与运算,1即为00000001 
}
void setBit(char &c, int i, int v)//这样实参也能改变 
{
	if(v)
		c |= (1 << i);
	else
		c &= ~(1 << i);
		//使右边的式子只有第i位为0,和c与了之后其实就是c的第i位变0,其余都不变 
} 

void flipBit(char &c, int i)//开关的翻转
{
	c ^= (1 << i);//按位异或 
} 

void output(int t, char result[])//输出第t组测试数据的结果 
{
	cout << "PUZZLE #" << t << endl;
	for(int i = 0; i < 5; i++)
	{
		for(int j = 0; j < 6; j++)
		{
			cout << getBit(result[i], j);
			if(j < 5)
				cout << ' ';
		}
		cout << endl;
	}		
} 

int main()
{
	int T;
	cin >> T;
	for(int t = 1; t <= T; t++){
		for(int i = 0; i < 5; i++)
			for(int j = 0; j < 6; j++)
			{
				int s;
				cin >> s;
				setBit(initLight[i], j, s);//将原始数据读入 
			}
		//枚举第一行所有开关的状态,用一个二进制整型int数从0变到2的n次方-1即可
		for(int n = 0; n < 64; n++)
		{
			int switchs = n;
			memcpy(lights, initLight, sizeof(initLight));
			for(int i = 0; i < 5; i++)
			{
				result[i] = switchs;
				for(int j = 0; j < 6; j++)//对第i行的灯进行处理 
				{
					if(getBit(switchs, j))
					{
						if(j > 0)
							flipBit(lights[i], j - 1);
						flipBit(lights[i], j);
						if(j < 5)
							flipBit(lights[i], j + 1);	
					}
				}
				if(i < 4)
					lights[i + 1] ^= switchs;//注意这里位异或的含义
				switchs = lights[i]; 
			}
			//到这里我们可以确定前四行的灯都是灭的,所以只需要判断第五行
			if(lights[4] == 0){
				output(t, result);
				break;
			}
		}
	}
	return 0;
} 

5 小结

整个代码采用了二进制数进行枚举,以及列举了各种位运算的方法,通过确定第一行来随之确定其余各行,虽说是枚举,但我们仍然应该将枚举的数量尽量的减少,以减少不必要的时间消耗。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mypollyanna

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值