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 }