POJ 1222(高斯消元解决开关问题-详解)

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.

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.

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.

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

 

前几日给师弟师妹们讲这个问题,一部分能听的明白,一部分听不明白,可能是自己表述有问题吧,那么就把今日份的无聊送给这道题的详解吧。


先说一下题意:给出一个5*6的0-1矩阵,0代表该位置的灯灭,1代表灯亮。

现在要对该矩阵进行一系列的操作,使得最后的灯变为全灭状态。

操作的含义是:当对位置(i,j)的灯进行操作时,该位置的灯的状态会变为原来的对立状态,即亮变为灭,灭变为亮,同时该位置上下左右四个灯泡也会做出同等的相应改变。

当然,如果该灯位于边界,只会影响三个灯泡或者两个灯泡的状态。


现在我们分析这道题,即 每个开关都会有一个操作,并且这个操作还会影响其他的开关,对于每个开关,只会有两种情况,操作或者不操作,因为操作两次等价于不操作。所以对于每个开关,要么操作一次,要么不进行操作。

下面举一个3*3的矩阵作为例子:

设有初始矩阵

                                                        

现在我们要对位置(2,2)的灯泡进行操作,操作之后,矩阵变为

                                                          

看这两个矩阵有什么关系?

是不是相当于 在原矩阵的基础上异或上一个矩阵

再或者操作(1,1),在原矩阵基础上异或一个矩阵

即,操作灯泡(i,j)等价于异或一个 (i,j)以及上下左右元素都为1,其它元素都为0的矩阵。

 

那么现在假设原矩阵为L,对于矩阵上某个位置进行的操作为A(i,j),是否进行操作为 O(i,j)

 

对上句话进行说明:A(i,j)就代表对位置(i,j)灯泡进行异或的矩阵,比如

A(1,1)=     A(2,2)=

O(i,j)代表对这个位置是否进行操作,即O(i,j)为0-1矩阵(要么操作为1,要么不操作为0)

 

所以我们可根据上述列出方程

(L)xor(A(1,1)*O(1,1))xor(A(1,2)*O(1,2))xor(A(1,3)*O(1,3))xor(A(2,1)*O(2,1))xor(A(2,2)*O(2,2))xor(A(2,3)*O(2,3))xor(A(3,1)*O(3,1))xor(A(3,2)*O(3,2))xor(A(3,3)*O(3,3)) =  0

把方程展开,对每个位置进行异或,就构造出了方程组。

只要构造出了方程组,那么套上高斯消元的板子,就可以解决问题了~!

 

高斯消元模板

 

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<algorithm>
#define max(a,b)   (a>b?a:b)
#define min(a,b)   (a<b?a:b)
#define swap(a,b)  (a=a+b,b=a-b,a=a-b)
#define memset(a,v)  memset(a,v,sizeof(a))
#define X (sqrt(5)+1)/2.0  //Wythoff
#define Pi acos(-1)
#define e  2.718281828459045
#define eps 1.0e-8
using namespace std;
typedef long long int LL;
typedef pair<int,int>pa;
const int MAXL(50);
const int INF(0x3f3f3f3f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
int a[MAXL+50][MAXL+50];//增广矩阵
int x[MAXL+50];//解集
bool free_x[MAXL];//标记是否是不确定的变元

void Gauss(int equ,int var)
{
    memset(x,0);
    int maxr,col;
    int k;
    for(k=0,col=0; k<equ&&col<var; k++,col++)
    {
        maxr=k;
        for(int i=k+1; i<equ; i++)
            if(abs(a[i][col])>abs(a[maxr][col]))
                maxr=i;
        if(maxr!=k)
            for(int j=k; j<var+1; j++)
                swap(a[k][j],a[maxr][j]);
        if(!a[k][col])
        {
            k--;
            continue;
        }
        for(int i=k+1; i<equ; i++)
        {
            if(a[i][col])
            {
                for(int j=col; j<var+1; j++)
                    a[i][j]^=a[k][j];
            }
        }
    }
    for(int i=var-1; i>=0; i--)
    {
        x[i]=a[i][var];
        for(int j=i+1; j<var; j++)
            x[i]^=(a[i][j]&&x[j]);
    }
    return;
}
int main()
{
    int T,CASE=1;
    scanf("%d",&T);
    while(T--)
    {
        memset(a,0);
        for(int i=0; i<30; i++)
            scanf("%d",&a[i][30]);
        for(int i=0; i<5; i++)
        {
            for(int j=0; j<6; j++)
            {
                int k=i*6+j;
                a[k][k]=1;
                if(i>0)
                    a[(i-1)*6+j][k]=1;//上
                if(i<4)
                    a[(i+1)*6+j][k]=1;//下
                if(j>0)
                    a[i*6+j-1][k]=1;  //左
                if(j<5)
                    a[i*6+j+1][k]=1;  //右
            }
        }
        Gauss(30,30);
        cout<<"PUZZLE #"<<CASE++<<endl;
        for(int i=0; i<5; i++)
        {
            int temp=i*6;
            cout<<x[temp];
            for(int j=1; j<6; j++)
            {
                temp=i*6+j;
                cout<<" "<<x[temp];
            }
            cout<<endl;
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值