首页 > 技术文章 > hihoCoder #1196 高斯消元·二

xcw0754 2015-08-16 10:57 原文

 

 

 

题意:如上图的一块板子,其中部分格子已经亮起,每当按下一个格子,其上下左右及自己共5个格子的亮暗状态会改变(亮变暗,暗变亮)。给定一块板子的初始状态,问要使得每个格子都亮起来,需要按多少次,以及按哪些格子?

 

 

 

思路:

 

  (1)对于任意一个格子,如果这个格子改变了偶数次状态,则等价于没有发生改变。我们可以将1看作格子亮着,0看作格子暗着,每改变1次就加1,最后格子的状态等于其总数值 MOD 2。则其运算结果刚好满足异或运算,即每改变一次等于状态值 xor 1。

  (2)对于一个格子x和它周围的4个格子来说,若格子x被按下偶数次,它自身和周围4个格子的状态也等于没有发生改变。所以我们可以知道:任意一个格子至多被按下一次

  (3)假设有数组x[1..30],分别表示这30个格子是否按下1次,若按下则x[i]=1,否则x[i]=0。则对于1个格子,他最后的状态为:

      当前状态 = 初始状态 xor (a[1] * x[1]) xor (a[2] * x[2]) xor ... xor (a[30] * x[30]);

    a[i]表示第i个格子是否受这个格子的影响,按一次最多只能影响5个,若在边界上可能还更少。

 

    因为我们的目标是要让所有等格子都为亮的状态,故我们需要让 当前状态 = 1,再将式子变换一下:

      (a[1] * x[1]) xor (a[2] * x[2]) xor ... xor (a[30] * x[30]) = 1 xor 初始状态;

    那么右边的就完全知道了,而左边只是知道a数组,要求的就是x数组了。上面仅仅是由一个格子所列出的式子,所以一共得列出30条式子才行,再进行高斯消元。大概是如下的:

      (a[ 1][1] * x[1]) xor (a[ 1][2] * x[2]) xor ... xor (a[ 1][30] * x[30])   = y[1];

      (a[ 2][1] * x[1]) xor (a[ 2][2] * x[2]) xor ... xor (a[ 2][30] * x[30])   = y[2];

      .......此处省略27条式子.....................

      (a[30][1] * x[1]) xor (a[30][2] * x[2]) xor ... xor (a[30][30] * x[30]) = y[30];

 

  (4)消元方法:方法和普通消元方法差不多,唯一变的是,若要消去a[j][i],不再用加减法,而是用xor。将第i行与第j行分别左边与左边,右边与右边相异或,作为第j行的结果。

    (a[j][1] * x[1]) xor (a[j][2] * x[2]) xor ... xor (a[j][30] * x[30]) xor (a[i][1] * x[1]) xor (a[i][2] * x[2]) xor ... xor (a[ i][30] * x[30]) = y[j] xor y[i]

    => ((a[j][1] * x[1]) xor (a[i][1] * x[1])) xor (((a[j][2] * x[2]) xor (a[i][2] * x[2]))) xor ... xor ((a[j][30] * x[30]) xor (a[i][30] * x[30])) = y[j] xor y[i]

      => ((a[j][1] xor a[i][1]) * x[1]) xor ((a[j][2] xor a[i][2]) * x[2]) xor ... ((a[j][30] xor a[i][30]) * x[30]) = y[j] xor y[i]

  

    总的来说,就是((a[j][1] * x[1]) xor (a[i][1] * x[1]))可以变成 ((a[j][1] xor a[i][1]) * x[1]) 。注意,如果a[j][i]本来就为0,这一步完全可省。消完之后从后面开始逐步进行代入就可以求出所有的x[i]了。

 

 

 

  

 1 #include <bits/stdc++.h>
 2 #define max(X,Y) ((X) > (Y) ? (X) : (Y))
 3 #define min(X,Y) ((X) < (Y) ? (X) : (Y))
 4 #define pii pair<int,int>
 5 #define INF 0x7f7f7f7f
 6 #define LL long long
 7 using namespace std;
 8 const int N=30;
 9 const int M=50;
10 int x[M];
11 int r[M];
12 int y[M];
13 int a[M][M];
14 
15 
16 void GE()
17 {
18     memset(a, 0, sizeof(a));
19     memset(y, 0, sizeof(y));
20     memset(x, 0, sizeof(x));
21 
22     for(int i=1; i<=N; i++) //初始化
23         y[i]=1^r[i];        //等式右边
24 
25     for(int i=1; i<=N; i++) //第i个格子的四周4个
26     {
27         a[i][i]=a[i][i+6]=1;
28         if(i-6>0)   a[i][i-6]=1;
29         if(i%6!=0)  a[i][i+1]=1;
30         if(i%6!=1)  a[i][i-1]=1;
31     }
32 
33     for(int i=1; i<=N; i++)
34     {
35         for(int j=N; j>=i; j--)   //找到某行第i列非0的,换到第i行。
36         {
37             if(a[j][i])
38             {
39                 swap(a[i], a[j]);
40                 swap(y[i], y[j]);
41                 break;
42             }
43         }
44 
45         //消去其他行的第i列
46         for(int j=i+1; j<=N; j++)
47         {
48             if(a[j][i]) //为1的才要消
49             {
50                 a[j][i]=0;
51                 for(int k=i+1; k<=N; k++)    a[j][k]^=a[i][k];
52                 y[j]^=y[i];
53             }
54         }
55     }
56 
57     //回代
58     for(int i=N; i>=1; i--) //对于第i行,只需要算出第i列的x。
59     {
60         for(int j=N; j>i; j--)    y[i]^=x[j]*a[i][j];   //反向回代
61         x[i]=y[i];  //a[i][i]已经保证为1了。
62     }
63 
64 }
65 
66 int main()
67 {
68     freopen("input.txt", "r", stdin);
69     char c;
70     for(int i=1; i<=N; i++)
71     {
72         c=getchar();
73         if(c=='0' || c=='1')  r[i]=c-'0';
74         else  i--;
75     }
76 
77     GE();
78 
79     int cnt=0;
80     for(int i=1; i<=N; i++) if(x[i])    cnt++;
81     printf("%d\n", cnt);
82     for(int i=1; i<=N; i++) if(x[i])    printf("%d %d\n", (i-1)/6+1, (i-1)%6+1);
83 
84     return 0;
85 }
AC代码

 

推荐阅读