试题连接:http://poj.org/problem?id=1222
先想到的是枚举,可是30个开关如果直接枚举的话,2^(30)肯定超时,那么看是否能找到一个局部, 当这个局部被确定后,剩余其他部分状态只能是确定的一种。
经过观察会发现,第1行就是这样一个局部,即第1行开关一旦确定,其余几行开关状态也会确定。(因为第一行开关作用以后,还会有几盏灯未熄灭,这时就需要下一行对应位置的开关按下,来熄灭上一行未熄灭的灯,如此往复,直到最后一行,如果最后一行灯正好全部熄灭,则该方案可行,否则不可行。
这里用位运算,可以更加简便。每一行只有6个灯,且状态都是用 0,1表示,而char 有8个bite,所以一个char 类型就可以表示一个灯的状态,(将6个灯的状态存在char类型的6个bite里面)。用长度为5的char型数组就可以装下灯的状态。
第一行6个灯,共有2^(6)=64种开关状态,而从 0~2^(6)-1 这64个数中的每一数的二进制 都可以表示开关的一个0 1组合
即从 00000000~11111111,
---------------------------------------------------------------------------------------------------
关于位运算可以先复习一下:http://blog.csdn.net/morewindows/article/details/7354571
基本的位操作符有与、或、异或、取反、左移、右移这6种,它们的运算规则如下所示:
符号 |
描述 |
运算规则 by MoreWindows |
& |
与 |
两个位都为1时,结果才为1 |
| |
或 |
两个位都为0时,结果才为0 |
^ |
异或 |
两个位相同为0,相异为1 |
~ |
取反 |
0变1,1变0 |
<< |
左移 |
各二进位全部左移若干位,高位丢弃,低位补0 |
>> |
右移 |
各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
特别强调:0^其他,不变;1^其他,逆置
#include <iostream>
#include <cstring>
using namespace std;
char orlight[5]; //灯原始状态
char light[5]; //对灯的操作都在此数组进行
char result[5];
int GetBit(char c,int i) //读取灯的状态
{
return (c>>i)&1;
}
void SetBit(char &c,int i,int v) //设置灯的状态
{
if(v)
c|=(1<<i);
else
c&=~(1<<i);
}
void FlipBit(char &c,int i) //翻转开关
{
c^=(1<<i); //0^(异或)其他,不变,1^(异或)其他,逆置
}
void OutputResult(int t,char result[]) //输出结果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()
{
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(orlight[i],j,s); //设置第i行,第j列的灯的状态
}
}
for(int n=0; n<64; n++)
{
int switchs=n; //switchs 是0~63的整数,用其二进制数 表示灯的情况!!!
//将 orlight数组拷贝到 light数组中,对灯的操作全在light中进行
memcpy(light,orlight,sizeof(orlight));
for(int i=0; i<5; i++)
{
result[i]=switchs;
for(int j=0; j<6; j++)
{
if(GetBit(switchs,j))
{
FlipBit(light[i],j);
if(j>0) //如果开关左边有灯,则将其左边翻转
FlipBit(light[i],j-1);
if(j<5) //如果开关右边有灯,则将其左边翻转
FlipBit(light[i],j+1);
}
}
if(i<4)
light[i+1]^=switchs; //下一行会受到上一行开关的影响
switchs=light[i]; //更新下一行的开关状态,其状态取决于上一行那些灯亮了
}
if(light[4]==0)
{//如果最后一行灯全熄灭,则输出
OutputResult(t+1,result);
}
}
}
return 0;
}