题目传送门
题目描述:
有一个由按钮组成的矩阵,其中每行有6个按钮,共五行
每个按钮位置上有一盏灯
当按下一个按钮后,该按钮以及周围位置(上边,下边,左边,,右边)灯都会改变状态
如果灯原来是点亮的,就会被熄灭
如果灯原来是熄灭的,就会被点亮
- 在矩阵角上的按钮会改变三盏灯的状态
- 在矩阵边上的灯会改变4盏灯的状态
- 其他的按钮改变五盏灯的状态
与一盏灯毗邻的多个按钮被按下时,一个操作会抵消另一个操作的结果
给定每盏灯的初始状态,求一种按钮方案,使得所有的灯都熄灭
如果要枚举所有的情况有2^30种,这是不可能的。所以还要寻找一些规律:
当第一行的switchs已经确定了,操作之后:
that means,只要第一行确定怎么按,其他行都是确定的。
如果位置(1, j)上的灯是点亮的,则要按下位置(2, j)上按钮,即Lights[2][j]一定取1;
如果位置(1, j)上的灯是熄灭的,则不能按位置(2, j)上按钮,即Lights[2][j]一定取0。
这样依据Lights的第一、二行操作矩阵中的按钮,才能保证第一行的灯全部熄灭。依此类推,可以确定switchs第三 四 五 行的值。
所以具体实现方法就是:先枚举出switchs第一行的所有情况,总共有2^6=64种。再根据 上面的规律确定switchs第三 四 五 行的值。最后判断第五行的灯是否全部熄灭,若全部熄灭则找到了答案。 代码实现如下:
这题主要是学会了关于二进制数的位运算
#include<iostream>
#include<stdio.h>
#include<string>
#include<memory>
#include<cstring>
using namespace std;
int GetBit(char c,int i)
{
//取c的第i位
return (c>>i)&1;
}
void SetBit(char &c,int i,int v)
{
//设置c的第i位为v
if(v)
c|=(1<<i);
else
c&=~(1<<i);
}
void Flip(char &c,int i)
{
//将c的第i位取反
c^=(1<<i);
}
void OutputResult(int t,char result[])
{
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()
{
char oriLights[5];
char Lights[5];
char result[5];
char switchs;
int T;
cin>>T;
for(int t=0; t<T; t++)
{
//读入每个灯的初始状态
for(int i=0; i<5; i++)
{
for(int j=0; j<6; j++)
{
int s;
cin>>s;
SetBit(oriLights[i],j,s);
}
}
for(int n=0; n<64; n++)
{
switchs=n;
memcpy(Lights,oriLights,sizeof(oriLights));
for(int i=0; i<5; i++)
{
result[i]=switchs;
for(int j=0; j<6; j++)
{
if(GetBit(switchs,j))
{
if(j>0)
{
Flip(Lights[i],j-1);
}
Flip(Lights[i],j);
if(j<5)
Flip(Lights[i],j+1);
}
}
if(i<4)
Lights[i+1]^=switchs;
switchs=Lights[i];
}
if(Lights[4]==0)
{
OutputResult(t+1,result);
break;
}
}//n 0-64 的那个循环
}
return 0;
}