题意:有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。
思路:令a[i][j]为不同灯泡的对应关系,默认a[i][i]=1,若a[i][j]=1,则说明第i个灯泡变化时第j个灯泡也改变。可以列出异或方程组
xi即为操作,1是改变该灯泡,0是不变。本题是求解的个数。由线性代数知识可以知道:
线性方程组的系数矩阵为A,增广矩阵为(A,B),则
当r(A)=r(A,B)=n时,有唯一解。
当r(A)=r(A,B)<n时,有2^(n-r)组解(x只有1,0两种情况,所以是2^(n-r)个解吧,我猜的)
当r(A)!=r(A,B)时,无解。
今天下午poj崩了,代码交不上去,下面代码不一定对
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
int t, n, x, y, a[100][100], s[100], e[100], b[100];
int cmp(int i, int j) {
int ai = 0, aj = 0;
for(int k = n; k > 0; k--) {
ai += (a[i][k] << (n - k));
aj += (a[j][k] << (n - k));
}
return aj > ai;
}
int main() {
cin >> t;
while(t--) {
cin >> n;
memset(a, 0, sizeof a);
for(int i = 1; i <= n; i++)
cin >> s[i], a[i][i] = 1;
for(int i = 1; i <= n; i++)
cin >> e[i], b[i] = s[i] ^ e[i];
while(cin >> x >> y && (x + y)) {
a[y][x] = 1;
}
int ans = 1;
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++)
if(cmp(i, j)) {
for(int k = 1; k <= n; k++)
swap(a[i][k], a[j][k]);
swap(b[i], b[j]);
}
int f = 0;
for(int j = 1; j <= n; j++) {
if(a[i][j])
f = 1;
}
if(f == 0 && b[i] == 1) {
ans = 0;
break;
}
if(f == 0 && b[i] == 0) {
ans = (1 << (n - i + 1));
break;
}
for(int k = 1; k <= n; k++)
if(a[i][k]) {
for(int j = 1; j <= n; j++)
if(i != j && a[j][k]) {
for(int s = 1; s <= n; s++)
a[j][s] ^= a[i][s];
b[j] ^= b[i];
}
break;
}
}
if(ans == 0)
puts("Oh,it's impossible~!!");
else
printf("%d\n", ans);
}
return 0;
}
由于只有01两个状态并且只有29的数组,所以可以进行状态压缩,可以用int的每一个二进制位代表一个状态。
#include<cstdio>
#include<algorithm>
using namespace std;
int t, n, a[41], ans, x, y;
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", a + i);
for(int j, i = 1; i <= n; i++) {
scanf("%d", &j);
a[i] ^= j;
a[i] |= (1 << i);//a[i][i]=1;
}
while(~scanf("%d%d", &x, &y) && (x + y)) {
a[y] |= (1 << x);//a[y][x]=1
}
ans = 1;
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++)
if(a[j] > a[i])
swap(a[i], a[j]);
if(!a[i]) {
ans = 1 << (n - i + 1);//只有i-1个主元
break;
}
if(a[i] == 1) {//只有常数项不是0
ans = 0;
break;
}
for(int k = n; k; k--)
if(a[i] >> k & 1) {
for(int j = 1; j <= n; j++)
if(i != j && (a[j] >> k & 1))
a[j] ^= a[i];
break;
}
}
if(ans == 0)
puts("Oh,it's impossible~!!");
else
printf("%d\n", ans);
}
return 0;
}