• poj 1222 EXTENDED LIGHTS OUT 高斯消元法


    http://162.105.81.212/JudgeOnline/problem?id=1222

    题意:有一个5*6的方阵,每个位置都表示按钮和灯,1表示亮,0表示灭。每当按下(i,j)时,(i,j)和(i-1,j)、(i+1,j)、

    (i,j-1)(i,j+1)都会改变,亮的变灭,灭的变亮;问在这样的一个方阵中按下哪些按钮可以把整个方阵都变成灭的,这时1表示按了,

    0表示没按。

    此题也可以用枚举来写:http://iiacm.net/2010/07/pku-1222-extended-lights-out/

    转载分析:这个游戏的名字叫做Lights Out。一个板子上面有MxN个按钮,按钮也是灯。每次按下一个按钮,这个按钮和它的上下左右相邻按钮将同时切换各自的亮灭状态。给你一个初始状态,请给出一种方法,按某些按钮,使得所有的灯都灭。

    这个游戏有一些技巧:
    1、按按钮的顺序可以随便。
    2、任何一个按钮都最多需要按下1次。因为按下第二次刚好抵消第一次,等于没有按。

    这个问题可以转化成数学问题。
    一个灯的布局可以看成一个0、1矩阵。以3x3为例:
    0 1 0
    1 1 0
    0 1 1
    表示一个布局。其中0表示灯灭,1表示灯亮。
    每次按下按钮(POJ1222)或者叫一个宿舍关灯(0998),可以看成在原矩阵上加(模2加,就是按位异或)上一个如下的矩阵:
    0 1 0
    1 1 1
    0 1 0
    上述矩阵中的1表示按下第2行第2列的按钮时,作用的范围。如果按左上角的按钮,就是:
    1 1 0
    1 0 0
    0 0 0

    我们记L为待求解的原始布局矩阵。A(i,j)表示按下第i行第j列的按钮时的作用范围矩阵。在上述例子中,
    L=
    0 1 0
    1 1 0
    0 1 1

    A(1,1)=
    1 1 0
    1 0 0
    0 0 0

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

    假设x(i,j)表示:想要使得L回到全灭状态,第i行第j列的按钮是否需要按下。0表示不按,1表示按下。那么,这个游戏就转化为如下方程的求解:
    L + x(1,1)*A(1,1) + x(1,2)*A(1,2) + x(1,3)*A(1,3) + x(2,1)*A(2,1) + ... + x(3,3)*A(3,3) = 0

    其中x(i,j)是未知数。方程右边的0表示零矩阵,表示全灭的状态。直观的理解就是:原来的L状态,经过了若干个A(i,j)的变换,最终变成0:全灭状态。
    由于是0、1矩阵,上述方程也可以写成:
    x(1,1)*A(1,1) + x(1,2)*A(1,2) + x(1,3)*A(1,3) + x(2,1)*A(2,1) + ... + x(3,3)*A(3,3) = L

    这是一个矩阵方程。两个矩阵相等,充要条件是矩阵中每个元素都相等。将上述方程展开,便转化成了一个9元1次方程组:

    简单地记做:AA * XX = LL

    这个方程有唯一解:
    x(1,1) x(1,2) x(1,3)
    x(2,1) x(2,2) x(2,3)
    x(3,1) x(3,2) x(3,3)
    =
    1 1 1
    0 0 0
    0 0 1

    也就是说,按下第一行的3个按钮,和右下角的按钮,就

    能使L状态变成全灭状态。
    对于固定行列的阵列来说,AA矩阵也是确定的。是否存在解,解是否唯一,只与AA矩阵有关。对于唯一解的情形,只要将LL乘以AA的逆矩阵即可。具体求AA的逆矩阵的方法,可以用高斯消元法。
    由于是0、1矩阵,上述方程也可以写成:

    将1式两边同时加上一个L矩阵就可以变成
    x(1,1)*A(1,1) + x(1,2)*A(1,2) + x(1,3)*A(1,3) + x(2,1)*A(2,1) + ... + x(3,3)*A(3,3) = L

    A(1,1)把矩阵 转化为一个列向量,L也转化为一个列向量,

    将sigma xi*Ai=Li 对应位置的值相等就可以建立方程组了

    X1*A(1,1)1+X2*A(1,2)1+X3*A(1,3)1+…………X30*A(30,30)1=L1;    mod 2

    X1*A(1,1)2+X2*A(1,2)2+X3*A(1,3)2+…………X30*A(30,30)2=L2;    mod 2

    X1*A(1,1)3+X2*A(1,2)3+X3*A(1,3)3+…………X30*A(30,30)3=L3    mod 2

    …….

    …….

    …….

    X1*A(1,1)30+X2*A(1,2)30+X3*A(1,3)30+…………X30*A(30,30)30=L30; mod 2

    其中A(i,j)k 表示列向量A中第K个元素

    这里的*表示点乘,Xi取(1,0) +表示模2加法,所以在高斯消元的时候可以用^异或运算

    view plaincopy to clipboardprint?
    01.#include<iostream>  
    02.using namespace std;  
    03.int map[35][35];  
    04.int ans[35];  
    05.int Gauss()     //高斯消元法  
    06.{  
    07.    int i, j, k, r;  
    08.    for(k=0; k<30; k++)  
    09.    {  
    10.        i = k;  
    11.        while(i<30 && map[i][k]==0)  i++;  
    12.        if(i > k)  
    13.        {  
    14.            for(r=0; r<=30; r++)  
    15.                swap(map[i][r], map[k][r]);  
    16.        }  
    17.        for(i=0; i<30; i++)  
    18.        {  
    19.            if(i != k && map[i][k])  
    20.            {  
    21.                for(j=0; j<=30; j++)  
    22.                    map[i][j] ^= map[k][j];     //高斯消元的^异或运算;  
    23.            }  
    24.        }  
    25.    }  
    26.    /*for(i=0; i<30; i++) 
    27.    { 
    28.        for(j=0; j<30; j++) 
    29.            printf("%d",map[i][j]); 
    30.        printf("\n"); 
    31.    }*/ 
    32.    for(i=0; i<30; i++)      //求出结果;  
    33.        if(map[i][30])  
    34.        {  
    35.            for(j=0; j<30 && !map[i][j]; j++)  ;  
    36.            if(j == 30) return 0;  
    37.            else 
    38.                ans[j] = map[i][30];  
    39.        }  
    40.    return 1;  
    41.}  
    42.int main()  
    43.{  
    44.    int i,j,k,kn,km,kx,ky,t;  
    45.    scanf("%d",&t);  
    46.    for(k=1; k<=t; k++)  
    47.    {  
    48.        memset(map, 0, sizeof(map));  
    49.        for(i=0; i<30; i++)  
    50.        {  
    51.            scanf("%d", &map[i][30]);  
    52.            ans[i] = 0;  
    53.        }  
    54.        /*for(i=0; i<30; i++)    //这个也是构造方程的,但是不知道为什么错了; 
    55.        { 
    56.            map[i][i] = 1; 
    57.            if(i % 6 != 1) map[i-1][i] = 1; 
    58.            if(i % 6 != 0) map[i+1][i] = 1; 
    59.            if(i >= 6) map[i-1][i] = 1; 
    60.            if(i <= 23) map[i+6][i] = 1; 
    61.        }*/ 
    62.        for(i=0; i<30; i++)      //构造30个方程  
    63.        {  
    64.            kn = i / 6;  
    65.            km = i % 6;  
    66.            for(j=0; j<30; j++)  
    67.            {  
    68.                kx = j / 6;   
    69.                ky = j % 6;  
    70.                if(abs(kx - kn) + abs(ky - km) <= 1)  
    71.                    map[i][j] = 1;  
    72.                else 
    73.                    map[i][j] = 0;  
    74.            }  
    75.        }  
    76.        /*for(i=0; i<30; i++) 
    77.        { 
    78.            for(j=0; j<30; j++){ 
    79.            printf("%d", map[i][j]);} 
    80.            printf("\n"); 
    81.        }*/ 
    82.        Gauss();  
    83.        printf("PUZZLE #%d\n",k);  
    84.        for(i=0; i<30; i++)  
    85.        {  
    86.            printf("%d", ans[i]);  
    87.            if((i+1) % 6 == 0)  
    88.                printf("\n");  
    89.            else 
    90.                printf(" ");  
    91.        }  
    92.    /// printf("\n");  
    93.    }  
    94.    return 0;  
    95.} 
    #include<iostream>
    using namespace std;
    int map[35][35];
    int ans[35];
    int Gauss()  //高斯消元法
    {
     int i, j, k, r;
     for(k=0; k<30; k++)
     {
      i = k;
      while(i<30 && map[i][k]==0)  i++;
      if(i > k)
      {
       for(r=0; r<=30; r++)
        swap(map[i][r], map[k][r]);
      }
      for(i=0; i<30; i++)
      {
       if(i != k && map[i][k])
       {
        for(j=0; j<=30; j++)
         map[i][j] ^= map[k][j];  //高斯消元的^异或运算;
       }
      }
     }
     /*for(i=0; i<30; i++)
     {
      for(j=0; j<30; j++)
       printf("%d",map[i][j]);
      printf("\n");
     }*/
     for(i=0; i<30; i++)  //求出结果;
      if(map[i][30])
      {
       for(j=0; j<30 && !map[i][j]; j++)  ;
       if(j == 30) return 0;
       else
        ans[j] = map[i][30];
      }
     return 1;
    }
    int main()
    {
     int i,j,k,kn,km,kx,ky,t;
     scanf("%d",&t);
     for(k=1; k<=t; k++)
     {
      memset(map, 0, sizeof(map));
      for(i=0; i<30; i++)
      {
       scanf("%d", &map[i][30]);
       ans[i] = 0;
      }
      /*for(i=0; i<30; i++) //这个也是构造方程的,但是不知道为什么错了;
      {
       map[i][i] = 1;
       if(i % 6 != 1) map[i-1][i] = 1;
       if(i % 6 != 0) map[i+1][i] = 1;
       if(i >= 6) map[i-1][i] = 1;
       if(i <= 23) map[i+6][i] = 1;
      }*/
      for(i=0; i<30; i++)  //构造30个方程
      {
       kn = i / 6;
       km = i % 6;
       for(j=0; j<30; j++)
       {
        kx = j / 6;
        ky = j % 6;
        if(abs(kx - kn) + abs(ky - km) <= 1)
         map[i][j] = 1;
        else
         map[i][j] = 0;
       }
      }
      /*for(i=0; i<30; i++)
      {
       for(j=0; j<30; j++){
       printf("%d", map[i][j]);}
       printf("\n");
      }*/
      Gauss();
      printf("PUZZLE #%d\n",k);
      for(i=0; i<30; i++)
      {
       printf("%d", ans[i]);
       if((i+1) % 6 == 0)
        printf("\n");
       else
        printf(" ");
      }
     /// printf("\n");
     }
     return 0;
    }


    首先,我们可以把6*5个灯组成的矩阵看成是一个1*30的向量a 。

        然后,对于每一个开关 i ,我们也构造一个1*30的向量d(i),一个开关最多控制5个灯,其中开关状态改变则为1,不改变就为0,这样我们可以把30个开关的向量组成一个30*30的矩阵。

           我们在构造一个30*1的向量ans,也就是我们要求的结果,ans[ i ]为1,表示需要按下第 i个开关,0表示不需要。这样ans*d=a(mod 2),(d 是30*30 的矩阵)就转化为解方程的问题了。

           至于解方程,就没什么可说的了,就是用线代里面讲的方法就可以了。因为这里要模2,所以可以我们可以直接用计算机的异或运算。
     


    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/shiren_Bod/archive/2010/07/26/5766907.aspx

  • 相关阅读:
    实验2四则运算结对
    作业5 四则运算 测试与封装 5.1
    0909我对编译的看法
    P2602 [ZJOI2010]数字计数(递推+数位dp写法)
    模数的世界[数论]
    P2312[秦九韶+读入取模+哈希解方程]
    第三章 Python 的容器: 列表、元组、字典与集合
    第二章 Python 基本元素:数字、字符串、变量
    第一章 Python 之初探
    第四章 Python 外壳 :代码结构
  • 原文地址:https://www.cnblogs.com/zxj015/p/2740300.html
Copyright © 2020-2023  润新知