开灯游戏-位运算

题目链接
题目描述
有9盏灯与9个开关,编号都是1~9。

每个开关能控制若干盏灯,按下一次会改变其控制的灯的状态(亮的变成不亮,不亮变成亮的)。

具体如下:

第一个开关控制第二,第四盏灯;

第二个开关控制第一,第三,第五盏灯;

第三个开关控制第二,第六盏灯;

第四个开关控制第一,第五,第七盏灯;

第五个开关控制第二,第四,第六,第八盏灯;

第六个开关控制第三,第五,第九盏灯;

第七个开关控制第四,第八盏灯;

第八个开关控制第五,第七,第九盏灯;

第九个开关控制第六,第八盏灯。

开始时所有灯都是熄灭的,开关是关闭着的。要求按下若干开关后,使得只有4盏灯亮着。
输入

输出
输出所有可能的方案,每行一个方案,每一行有9个字符,从左往右第i个字符表示第i个开关的状态(" 0" 表示关闭," 1" 表示打开),按字典序输出。下面的样例输出只是部分方案。
样例输入

样例输出
000001011
000001110
000001111

思路
复习一手位运算
看到开关就想到二进制
拿开关一举例,他能使2,4亮,用二进制表示就是1010(从右边开始往左数),十进制就是10
开关二,它能使1,3,5亮,用二进制表示就是10101,十进制就是21
以此类推
我们用一个a数组保存每一盏灯代表的十进制数
还需要一个变量res存储当前灯的状态,开始没亮res=0
dfs枚举灯的状态000 000 000-111 111 111,并把此时状态用一个数组tmp存储,遍历这个数组
如果tmp[i]=1,就说明按一下第i个开关,res^=a[i]就说明操作了开关i
遍历完成得到res,此时只要计算二进制表示的res有多少个1就行
res&(res-1)操作就是去掉res的末尾1,再来一个循环一次去掉末尾1并计数就完事了

#include<iostream>
#include<cstring>
#define maxx 511//111 111 111(二进制)=511(十进制)
using namespace std;
int a[10],ans;
void dfs(int status)
{
	if(status>maxx)
		return;
	int t=status,cnt=0,tmp[10],res=0,num=0;
	memset(tmp,0,sizeof(tmp));
	while(t)//得到开关状态 
	{
		tmp[++cnt]=(t&1);
		t>>=1;
	}
	for(int i=1;i<10;i++)//灯的状态 
	{
		if(tmp[i])
			res^=a[i];
	}
	while(res)//计算开灯数 
	{
		num++;
		res=res&(res-1);
	}
	t=status;
	if(num==4)
	{
		ans++;
		int b[10],i=0;
		memset(b,0,sizeof(b));
		while(t)
		{
			b[++i]=(t&1);
			t>>=1;
		}
		for(int j=9;j>0;j--)//我们保存开关的状态时是从右边开始编号的,故要颠倒一下
			cout << b[j];
		cout << endl; 
	} 
	dfs(status+1); 
}
int main()
{
	a[1]=10;a[2]=21;a[3]=34;a[4]=81;a[5]=170;a[6]=276;a[7]=136;a[8]=336;a[9]=160;
	dfs(1);//遍历状态000 000 000-111 111 111 
//	cout << ans;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值