题目链接
题目描述
有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;
}