poj1830 开关问题
这道题也是高斯消元解异或方程组,但是书上写的程序是有问题的。
用这个数据可以hack掉:
1
3
0 0 0
1 1 1
1 2
2 1
2 3
0 0
列出矩阵:
1 1 0 1
1 1 1 1
0 0 1 1
然后就会给出无解。实际上是有2解的。
下面来看正解:
异或方程组不能往上回消,也不能像书上那样每次把上下都消了。
直接消成上三角矩阵。
答案就是 (1 << 自由元个数)
关于无解的判定:自由元常数项和前面不统一时即无解。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 const int N = 35; 5 6 int a[N], n; 7 8 inline int Gauss() { 9 int ans = 0; 10 for(int i = 1; i <= n; i++) { 11 for(int j = i; j <= n; j++) { 12 if(a[j] & (1 << i)) { 13 std::swap(a[i], a[j]); 14 break; 15 } 16 } 17 if(!((a[i] >> i) & 1)) { 18 if((a[i] > 1) ^ (a[i] & 1)) { 19 return -1; 20 } 21 else { 22 ans++; 23 continue; 24 } 25 } 26 for(int j = i + 1; j <= n; j++) { 27 if(a[j] & (1 << i)) { 28 a[j] ^= a[i]; 29 } 30 } 31 } 32 return ans; 33 } 34 35 inline void solve() { 36 scanf("%d", &n); 37 int c, b; 38 for(int i = 1; i <= n; i++) { 39 scanf("%d", &a[i]); 40 } 41 for(int i = 1; i <= n; i++) { 42 scanf("%d", &b); 43 a[i] ^= b; 44 } 45 while(scanf("%d%d", &c, &b)) { 46 if(!c) { 47 break; 48 } 49 a[b] |= (1 << c); 50 } 51 for(int i = 1; i <= n; i++) { 52 a[i] |= (1 << i); 53 } 54 55 int t = Gauss(); 56 if(t == -1) { 57 printf("Oh,it's impossible~!!\n"); 58 return; 59 } 60 printf("%d\n", 1 << t); 61 return; 62 } 63 64 int main() { 65 int T; 66 scanf("%d", &T); 67 while(T--) { 68 solve(); 69 if(T) { 70 memset(a, 0, sizeof(a)); 71 } 72 } 73 return 0; 74 }
但是不管怎么说,有一点让我很在意:
高斯消元解异或方程组的时候,两行异或这个操作的实际意义是什么?
现在我可能有答案了。
没有意义,不要联系实际,而是当成方程的形式,在每一项后面配一个xj就行了。
然后联系这一题:
本题求方案数,所以可以直接用2^ans
但是那一题求最小值,而每个主元虽说固定,但是分选/不选两种。
然后自由元的选/不选虽然不会改变主元的个数,但是能改变主元的状态。
所以需要DFS。
但是还有个问题:为什么不能往上回消呢?
反正记住就好了。