POJ1222 EXTENDED LIGHTS OUT

Description

In an extended version of the game Lights Out, is a puzzle with 5 rows of 6 buttons each (the actual puzzle has 5 rows of 5 buttons each). Each button has a light. When a button is pressed, that button and each of its (up to four) neighbors above, below, right and left, has the state of its light reversed. (If on, the light is turned off; if off, the light is turned on.) Buttons in the corners change the state of 3 buttons; buttons on an edge change the state of 4 buttons and other buttons change the state of 5. For example, if the buttons marked X on the left below were to be pressed,the display would change to the image on the right. 

在游戏《熄灯》的扩展版本中,有一个5行且每一行有6个按钮的拼图(实际的拼图有5行,每一行有5个按钮)。每个按钮都有一盏灯。当一个按钮被按下时,这个按钮和它上面、下面、右边和左边的每个(最多四个)邻居的灯光状态就会被颠倒。(如果原来灯是亮着的,就被熄灭;如果原本灯是熄灭的,就被点亮了)。在角落的按钮会改变3个按钮的状态;在边缘上的按钮会改变4个按钮的状态,其他的按钮会改变5个按钮的状态。例如,如果按下下面左边标有X的按钮,则显示将更改为右边的图像。

The aim of the game is, starting from any initial set of lights on in the display, to press buttons to get the display to a state where all lights are off. When adjacent buttons are pressed, the action of one button can undo the effect of another. For instance, in the display below, pressing buttons marked X in the left display results in the right display.Note that the buttons in row 2 column 3 and row 2 column 5 both change the state of the button in row 2 column 4,so that, in the end, its state is unchanged. 

游戏的目的是,从显示器上的任何一组初始灯光开始,按下按钮,使显示器处于所有灯光都关闭的状态。当相邻的按钮被按下时,一个按钮的动作可以抵消另一个按钮的影响。例如,在下图左边的图中按下标有X的按钮后,灯光状态变化结果如右图所示。请注意,第2行第3列和第2行第5列中的按钮都更改了第2行第4列中的按钮的状态,因此最终其状态不变。

Note: 

1. It does not matter what order the buttons are pressed. 

2. If a button is pressed a second time, it exactly cancels the effect of the first press, so no button ever need be pressed more than once. 

3. As illustrated in the second diagram, all the lights in the first row may be turned off, by pressing the corresponding buttons in the second row. By repeating this process in each row, all the lights in the first four rows may be turned out. Similarly, by pressing buttons in columns 2, 3 ?, all lights in the first 5 columns may be turned off. 

Write a program to solve the puzzle.

注意:

1 按下按钮的顺序无关紧要。

2 如果按钮被再次按下,它就会完全抵消第一次按下的效果,所以任何按钮都不需要按一次以上。

3 如图二所示,第一行的所有灯都可以通过按第二行对应的按钮关闭。通过在每行重复这个过程,前四排的所有灯光都能够被关闭。同样地,在第2、3、4、5、6列中按下按钮,前5列中的所有灯都可能关闭。

写一个程序来解决这个难题。

Input

The first line of the input is a positive integer n which is the number of puzzles that follow. Each puzzle will be five lines, each of which has six 0 or 1 separated by one or more spaces. A 0 indicates that the light is off, while a 1 indicates that the light is on initially.

输入的第一行是正整数n,它是后面的字谜数。每个谜题有5行,每一行都有6个0或1被一个或多个空格隔开。0表示灯亮着,1表示灯亮着。

Output

For each puzzle, the output consists of a line with the string: "PUZZLE #m", where m is the index of the puzzle in the input file. Following that line, is a puzzle-like display (in the same format as the input) . In this case, 1's indicate buttons that must be pressed to solve the puzzle, while 0 indicate buttons, which are not pressed. There should be exactly one space between each 0 or 1 in the output puzzle-like display.

对于每个谜题,输出由一行字符串组成:“谜题#m”,其中m是输入文件中谜题的索引。在这一行之后,是一个类似于拼图的显示(与输入格式相同)。在本例中,1的指示按钮必须按下才能解谜,而0的指示按钮没有按下。在输出类似拼图的显示中,每个0或1之间应该有一个空格。

Sample Input

2

0 1 1 0 1 0

1 0 0 1 1 1

0 0 1 0 0 1

1 0 0 1 0 1

0 1 1 1 0 0

0 0 1 0 1 0

1 0 1 0 1 1

0 0 1 0 1 1

1 0 1 1 0 0

0 1 0 1 0 0

Sample Output

PUZZLE #1

1 0 1 0 0 1

1 1 0 1 0 1

0 0 1 0 1 1

1 0 0 1 0 0

0 1 0 0 0 0

PUZZLE #2

1 0 0 1 1 1

1 1 0 0 0 0

0 0 0 1 0 0

1 1 0 1 0 1

1 0 1 1 0 1

解题思路:枚举

想法1:枚举所有可能的开关状态,对每个状态计算一下最后灯的情况,看是否都熄灭。

--那么每个按钮有两种状态(按下或者不按下)

--一共有30个开关,那么状态数有2^30,太多,会超时。

如何减少枚举的状态数目呢?

基本思路:如果存在某个局部,一旦这个局部的状态被确定,那么剩余其它部分的状态只能是确定的一种,或者不多的n种,那么就只需枚举这个局部的状态即可。

想法2:

可以这样思考,首先在第一行开关状态确定的情况下,必然导致第一行的某些灯是亮着的或者熄灭的。

如果想要熄灭第一行第j列的灯,唯一的办法就是按下第二行第j列的灯,因此第二行的开关状态也被确定了。

以此内推,第三第四第五行的开关状态都是确定,只需要判断在所有开关状态执行结束后,第五行的灯是否全灭。

这样只需要枚举第一行的状态,状态数是2^6=64

编写代码思路:

本来我们应该考虑使用二维数组来存储输入的灯光状态和输出的按钮状态,但是因为在本题中二维数组存储的是0和1的数值,因此我们想到了使用二进制来存储以上信息。

其中几个关键的函数实现方式都灵活的利用了二进制的位操作:

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)  //v=1
        c |= (1 << i);
    else   //v=0
        c &= ~(1 << i);
}
void FlipBit(char & c, int i)  //翻转c的二进制的第i位  1变0  0变1
{
    c ^= (1<<i);
}

代码如下:

#include <iostream>
#include <memory>
#include <cstring>
#include <string>
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)  //v=1
        c |= (1 << i);
    else   //v=0
        c &= ~(1 << i);
}
void FlipBit(char & c, int i)  //翻转c的二进制的第i位  1变0  0变1
{
    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类型的数据恰好有8个二进制位,刚好可以存储一行输入状态
    char Lights[5];    //存储按钮的操作状态
    char result[5];    
    char switchs;
    int T;  //输入迷宫题集个数
    cin>>T;
    for(int t=1;t<=T;t++)
    {
        memset(oriLights, 0, sizeof(oriLights));
        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++)    //第一行有64种开关状态,刚好可以用64的二进制数来表示
        {
            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)){  //如果[i][j]位置的灯被按下,则会改变前后灯光状态
                        if(j>0)
                            FlipBit(Lights[i], j-1);
                        FlipBit(Lights[i], j);
                        if(j<5)
                            FlipBit(Lights[i], j+1);
                    }
                }
                if(i<4)   //第i行按钮被按下,也会影响第I+1行的灯光状态,在这里通过异或运算计算出了下一行灯光的状态。
                    Lights[i+1]^=switchs;
                switchs=Lights[i]; //确定了前一行的灯光状态后,下一行的按钮按下的状态应该和前一行的灯光状态一致,因为如果第i行第j列的灯光亮着,那么只能通过按下第i+1行第j列的按钮来改变其状态。
            }
            if(Lights[4]==0) //如果最后一行的灯光全灭,说明该组按钮按下的状态正确。
            {
                OutputResult(t, result);
                break;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_25406563/article/details/82670325