poj 1222 (枚举)

试题连接: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;
}


原文地址:https://www.cnblogs.com/zhanyeye/p/9746112.html