• hihoCoder#1196 : 高斯消元·二(开关灯问题)


    传送门

    高斯消元解异或方程组

    小Ho在游戏板上忙碌了30分钟,任然没有办法完成,于是他只好求助于小Hi。

    小Ho:小Hi,这次又该怎么办呢?

    小Hi:让我们来分析一下吧。

    首先对于每一个格子的状态,可能会对它造成影响的是其自身和周围4个格子,这五个格子被按下的总次数也就等于该格子所改变的总次数。

    对于任意一个格子,如果这个格子改变了偶数次状态,则等价于没有发生改变。

    我们可以将1看作格子亮着,0看作格子暗着,每改变1次就加1,最后格子的状态等于其总数值 MOD 2。

    则其运算结果刚好满足异或运算,即每改变一次等于状态值 xor 1。

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

    假设有数组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是否会对当前格子产生影响,若能够则a[i] = 1,否则a[i] = 0

    对方程进行变换有:

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

    因为我们的目标是要让所有等格子都为亮的状态,故我们需要让 当前状态 = 1,则:

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

    不妨设y = 1 xor 初始状态:

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

    对于所有的格子,我们可以连立出方程组:

    (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]
                                                ...
    (a[30][1] * x[1]) xor (a[30][2] * x[2]) xor ... xor (a[30][30] * x[30]) = y[30]
    		

    到此,我们的目标就是求出一个x[1..30],使得上面的方程组成立。

    小Ho:这个看上去和高斯消元很像啊。

    小Hi:没错,这个方程组叫异或方程组,它可以用和高斯消元同样的方法来解决。

    其解答过程几乎和高斯消元无异,判定无解和多解的方式也相同。唯一需要注意的是消元过程不再是高斯消元的加减,而是通过xor运算来进行消元。比如消除第j行第i列的1:

    a[j][k] = a[j][k] xor a[i][k], y[j] = y[j] xor y[i]

    其原理是:

        (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[i][j]矩阵一定是固定的,而且通过计算可以知道我们消元得到的上三角矩阵也是固定的,并且在这一次的问题中该上三角矩阵是满秩的,所以其一定存在唯一解。

    所以我们一定有办法完成这个游戏。

    小Ho:我明白了,我这就去写程序,这奖品我拿定了!

    #include <cmath>
    #include <cstdio>
    #include <iostream>
    #define N 35
    #define D(x, y) (((x) - 1) * 6 + (y))
    
    using namespace std;
    
    int ans;
    int a[N][N];
    char s[N][N];
    
    inline void Guass()
    {
    	int i, j, k, t;
    	for(j = 1; j <= 30; j++)
    	{
    		t = j;
    		for(i = j; i <= 30; i++)
    			if(a[i][j] > a[t][j])
    				t = i;
    		if(t != j) swap(a[t], a[j]);
    		for(i = j + 1; i <= 30; i++)
    			if(a[i][j])
    				for(k = j; k <= 31; k++)
    					a[i][k] ^= a[j][k];
    	}
    	for(i = 30; i >= 1; i--)
    	{
    		for(j = i + 1; j <= 30; j++)
    			a[i][31] ^= (a[i][j] * a[j][31]);
    		if(a[i][31]) ans++;
    	}
    }
    
    int main()
    {
    	int i, j;
    	for(i = 1; i <= 5; i++)
    	{
    		scanf("%s", s[i] + 1);
    		for(j = 1; j <= 6; j++)
    		{
    			a[D(i, j)][D(i, j)] = 1;
    			a[D(i, j)][31] = 1 ^ (s[i][j] - '0');
    			if(1 < i && i <= 5) a[D(i - 1, j)][D(i, j)] = a[D(i, j)][D(i - 1, j)] = 1;
    			if(1 <= i && i < 5) a[D(i + 1, j)][D(i, j)] = a[D(i, j)][D(i + 1, j)] = 1;
    			if(1 < j && j <= 6) a[D(i, j - 1)][D(i, j)] = a[D(i, j)][D(i, j - 1)] = 1;
    			if(1 <= j && j < 6) a[D(i, j + 1)][D(i, j)] = a[D(i, j)][D(i, j + 1)] = 1;
    		}
    	}
    	Guass();
    	printf("%d
    ", ans);
    	for(i = 1; i <= 5; i++)
    		for(j = 1; j <= 6; j++)
    			if(a[D(i, j)][31])
    				printf("%d %d
    ", i, j);
    	return 0;
    }
    

      

  • 相关阅读:
    关于APNs的错误认识纠正
    关于NSParagraphStyle
    关于离开UIScrollview所在界面时,UIScrollview的偏移量发生变化
    关于核心动画的一个提示
    关于PDF展示解决方案
    DQN 强化学习
    什么是 DQN
    面试时应该问面试官什么
    看demo1
    试着用教程跑cifar10数据
  • 原文地址:https://www.cnblogs.com/zhenghaotian/p/7494942.html
Copyright © 2020-2023  润新知