题目描述:有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。(不计开关操作的顺序)
下面是1830怎么和高斯消元联系起来:(转自他人blog,灰常经典)
对于每个灯有动它和不动它2种状态,用1,0表示,那么可以构造列向量X=[x1,x2....xn],Xi =0 或者1.
系数矩阵B[b1,b2...bn],变换矩阵A[n][n]; 假设 A*X = B,那么bi 等于
A的第i行元素和X对应相乘,那么我们可以这样来设置A[i][j]:
如果第j个灯操作会影响i,那么A[i][j]=1.
如果i的初始化状态和目标状态不同,那么b[i]=1.
因为b[i] 等于
A的第i行元素和X对应相乘。那么也就是说第i个灯最后的状态是不是会变是由X决定的,因为b[i] = sum(a[i][j] *
X[j]),这里的求和视为模2运算也就是^,所以第i个灯最后的状态和初始比是不是变了是由每一个和i有关的灯决定的,这也就是为什么j会影响i设A[i][j]
=
1的原因。也就是为什么要设b[i]是初始状态和最终状态的异或。。。。。。于是结果就变成了求方程A*X=B有多少个解。
有了上面的思路代码就很简单了。
#include<iostream>
using namespace std;
#define N 29
#define M 29
typedef int zzy;
zzy fab(zzy x) {return (x > 0 ? x : -x);}
void swap(zzy &x, zzy &y) { zzy
temp = x; x = y; y = temp;}
zzy A[N][M 1]; //N 行 M 1列:多出来的一列是答案b列,增广矩阵
int gaos(int r, int c)// c 为系数矩阵实际的列数,不带最后一列的增广部分
{
int i = 0, j
= 0;
while(i
< r && j
< c)
{
int id = i;
for(int k = i 1; k < r; k )
if (fab(A[k][j]) > fab(A[id][j]))
id = k;
zzy temp;
if (A[id][j] != 0)
{
temp = A[id][j];
for(int k = j; k < c 1; k )
swap(A[id][k], A[i][k]);
for(int k = i 1; k < r; k )
if (A[k][j] != 0)
for(int l = j; l < c 1; l )
A[k][l] = A[k][l] ^ A[i][l];
i ;
}
j ;
}
for(int k =
i; k < r; k )
if (A[k][c] != 0)
return -1; // 无解
return 1
<< (r - i);//无穷解
}
int a[30], b[30];
int main()
{
int t,
n;
scanf("%d",
&t);
while(t--)
{