poj 1222 EXTENDED LIGHTS OUT (高斯消元)

题目链接

看了好长时间这个题了,也大体弄明白了这个解题的步骤,但是这个方程的过程和为什么这么列还是不太清楚。。嗯,有点无奈

题意:给一个确定的5*6放入矩阵,每个格子都有一个开关和一盏灯,0表示灯没亮,1表示灯亮着。让你输出一个5*6的矩阵ans[i][j],ans[i][j] = 1表示按下开关,ans[i][j] = 0表示不按开关,使最后所有的灯都熄灭。

分析:

30个格子,30个方程,30个未知数。 把每个格子和与它相关联(这题就是它的上下左右)的表示为1,其它表示为0。

因为题目唯一解,所以模板里没有加多解和无解的情况。

比较详细的分析可以看这个博客:http://blog.csdn.net/u013081425/article/details/24452153

假设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 

初始看成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 的矩阵)就转化为解方程的问题了

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <cstdlib>
  5 #include <cmath>
  6 #include <algorithm>
  7 #define LL __int64
  8 const int maxn = 30+10;
  9 using namespace std;
 10 int equ, var, fn;
 11 int a[maxn][maxn], x[maxn];
 12 
 13 int gcd(int a, int b)
 14 {
 15     return b==0?a:gcd(b, a%b);
 16 }
 17 int lcm(int a, int b)
 18 {
 19     return a*b/gcd(a, b);
 20 }
 21 int Gauss()  //记得%2,因为只有0,1两种状态
 22 {
 23     int x_mo;
 24     x_mo = 2;
 25     int i, j, k, max_r, col;
 26     int ta, tb, LCM, tmp;
 27     col = 0;
 28 
 29     for(k = 0; k<equ && col<var; k++, col++)
 30     {
 31         max_r = k;
 32         for(i = k+1; i < equ; i++)
 33             if(abs(a[i][col])>abs(a[max_r][col]))
 34             max_r = i;
 35 
 36         if(max_r != k)
 37             for(j = k; j < var+1; j++)
 38             swap(a[k][j], a[max_r][j]);
 39 
 40         if(a[k][col]==0)
 41         {
 42             k--;
 43             continue;
 44         }
 45         for(i = k+1; i < equ; i++)
 46         {
 47             if(a[i][col] != 0)
 48             {
 49                 LCM = lcm(abs(a[i][col]), abs(a[k][col]));
 50                 ta = LCM/abs(a[i][col]);
 51                 tb= LCM/abs(a[k][col]);
 52                 if(a[i][col]*a[k][col] < 0) tb = -tb;
 53 
 54                 for(j = col; j < var+1; j++)
 55                 a[i][j] = ((a[i][j]*ta - a[k][j]*tb)%x_mo+x_mo)%x_mo;
 56             }
 57         }
 58     }
 59     for(i = var-1; i >= 0; i--)
 60     {
 61         tmp = a[i][var];
 62         for(j = i+1; j < var; j++)
 63         if(a[i][j] != 0)
 64         tmp = ((tmp-a[i][j]*x[j])%x_mo+x_mo)%x_mo;
 65 
 66         if(tmp%a[i][i] != 0) return -2;
 67         while(tmp%a[i][i]!=0) tmp += x_mo;
 68         x[i] = (tmp/a[i][i])%x_mo;
 69     }
 70     return 0;
 71 }
 72 int main()
 73 {
 74     int i, j, t, ca=1;
 75     scanf("%d", &t);
 76     while(t--)
 77     {
 78         equ = 30;
 79         var = 30;
 80         memset(a, 0, sizeof(a));
 81         memset(x, 0, sizeof(x));
 82 
 83         for(i = 0; i < 30; i++)
 84         scanf("%d", &a[i][30]);  //这个就是转置矩阵的最后一列
 85 
 86         for(i = 0; i < 5; i++)
 87         for(j = 0; j < 6; j++)
 88         {
 89             int tmp = i*6+j; //枚举的其实是原矩阵从上往下,从左往右的每一个
 90             a[tmp][tmp] = 1;
 91             if(tmp-6>=0)
 92             a[tmp-6][tmp] = 1; //表示第二个下标能影响第一个下标代表的灯,虽然这个题没影响,但是貌似不能倒过来。
 93             if(tmp+6<30)
 94             a[tmp+6][tmp] = 1;
 95             if(j>=1)
 96             a[tmp-1][tmp] = 1;
 97             if(j<=4)
 98             a[tmp+1][tmp] = 1;
 99         }
100         fn = Gauss();
101         printf("PUZZLE #%d\n", ca++);
102         for(i = 0; i < 30; i++)
103         {
104             if(i%6==5) printf("%d\n", x[i]);
105             else printf("%d ", x[i]);
106         }
107     }
108     return 0;
109 }

 

posted @ 2014-08-18 09:12  水门  阅读(201)  评论(0编辑  收藏  举报