Poj-1222-EXTENDED LIGHTS OUT-枚举

来源:互联网 发布:x431pro3s软件下载app 编辑:程序博客网 时间:2024/06/03 13:34

题面:
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.

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.

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.
大意:
给出一个nxn的棋盘,每个格子上有一盏灯和一个开关,按下开关后,本位置及上下左右的灯的状态就会反转(亮->灭,灭->亮)。问按下哪些开关可以使棋盘上的灯都熄灭。
AC代码:

int mat[6][7],cop[6][7],ans[6][7];//矩阵、矩阵的复制、答案//为什么要复制矩阵?因为我们需要针对每一种状态求解,状态不止一种,又不能破坏原矩阵,所以每进行一次枚举,就要复制一次矩阵void press(int x,int y)//按下会发生什么?{    cop[x][y] = !cop[x][y];//0变1,1变0    for(int i = 0; i <= 3; ++i)    {        int tx = x + dirx[i];        int ty = y + diry[i];        if(tx < 1 || tx > 5 || ty < 1 || ty > 6)//越界            continue;        cop[tx][ty] = !cop[tx][ty];    }    return;}void Copy()//复制矩阵{    for(int i = 1; i <= 5; ++i)        for(int j = 1; j <= 6; ++j)            cop[i][j] = mat[i][j];    return;}bool check()//检验答案{    for(int j = 1; j <= 6; ++j)        if(cop[5][j] == 1)//如果最后一行存在开着的灯            return false;//返回假    return true;}int main(){    int n,cnt = 1;    cin >> n;    while(n--)    {        //输入矩阵        for(int i = 1; i <= 5; ++i)            for(int j = 1; j <= 6; ++j)                cin >> mat[i][j];        //枚举第1行的状态,每个i就代表一个状态。        for(int i = 0; i <= (1 << 6) - 1; ++i)//000000---111111(0---2^6-1)        {            Copy();//复制            memset(ans,0,sizeof(ans));//重置            for(int j = 0; j <= 5; ++j)//对于第一行                if((1 << j) & i)//二进制的方法枚举子集                {                    press(1,j + 1);//6 - j与j + 1都行。仅仅是阅读二进制位的顺序不同而已                    ans[1][j + 1] = 1;                }            for(int j = 2; j <= 5; ++j)//对于接下来的4行                for(int k = 1; k <= 6; ++k)                    if(cop[j - 1][k] == 1)//如果灯开着                    {                        press(j,k);                        ans[j][k] = 1;                    }            if(check() == true)            {                printf("PUZZLE #%d\n",cnt++);                for(int i = 1; i <= 5; ++i)//打印答案                {                    for(int j = 1; j <= 6; ++j)                        printf("%d ",ans[i][j]);                    cout << endl;                }            }        }    }    return 0;}

解决方法:
①本问题还可以利用高斯消元法求解
②按下开关的顺序并不重要。类似于这样的情况我遇到好多,乍读完题,隐约觉得顺序是一种很复杂的东西,这样的感觉使我倾向于认为顺序是不可被忽略的。“顺序不重要”这个重要的条件会对解题提供很大的帮助。但是如何判断顺序重不重要呢。可以用数学的方法证明,任取开关1和开关二,先按下开关1再按下开关2和先按下开关2再按下开关1是一样的吗?
①开关1和开关2距离很远,它们能影响到的范围没有交集,显然结果是一样的
②开关1和开关2的距离并非很远,它们能影响到的范围有交集,无交集的部分依然不受顺序的影响,有交集的部分因为被翻转2次而保持原状,所以得到的结果是一样的
这样的方法推广到3个开关,4个开关……
综上所述,按下开关的顺序与最终的结果没有影响。
我们太容易受到感性的影响了,而理性的认识对我们才是更重要的。
③有了以上的结论,那么一个开关会被按下多次吗?如果这个开关被按下了2次,就相当于没按,如果这个开关被按下了3次,就相当于只被按下1次。所以一个开关要么被按下1次,要么没被按下过。
④对于一个问题,一开始我们很难下手将其一口气解决,我们可以找出其结果,通过缩小问题规模来将问题简化,从而找到算法,将问题一步步解决。这个过程就像多米诺骨牌一样。
⑤这道题可以通过枚举的方法来解决。通过枚举第一行开关被按下的每一种状态来找到答案。当第一行的开关被按下的状态确定后,第二行应该按下哪一些开关就被唯一确定了(如果第一行的某个灯开着,对应的下面的第二行的开关就必须被按下),以此类推。如果不进行枚举的话,情况将是数不胜数,难以入手。通过枚举,针对每一种情况求解,有了很强的针对性,题目就好做了。枚举大法有、好。
⑥这道题的代码,也有许多亮点。比如press函数,子集枚举,答案判断的函数、矩阵复制等等。

原创粉丝点击