任意门:http://poj.org/problem?id=1830
开关问题
Time Limit: 1000MS |
|
Memory Limit: 30000K |
Total Submissions: 10742 |
|
Accepted: 4314 |
Description
有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。(不计开关操作的顺序)
Input
输入第一行有一个数K,表示以下有K组测试数据。
每组测试数据的格式如下:
第一行 一个数N(0 < N < 29)
第二行 N个0或者1的数,表示开始时N个开关状态。
第三行 N个0或者1的数,表示操作结束后N个开关的状态。
接下来 每行两个数I J,表示如果操作第 I 个开关,第J个开关的状态也会变化。每组数据以 0 0 结束。
每组测试数据的格式如下:
第一行 一个数N(0 < N < 29)
第二行 N个0或者1的数,表示开始时N个开关状态。
第三行 N个0或者1的数,表示操作结束后N个开关的状态。
接下来 每行两个数I J,表示如果操作第 I 个开关,第J个开关的状态也会变化。每组数据以 0 0 结束。
Output
如果有可行方法,输出总数,否则输出“Oh,it's impossible~!!” 不包括引号
Sample Input
2 3 0 0 0 1 1 1 1 2 1 3 2 1 2 3 3 1 3 2 0 0 3 0 0 0 1 0 1 1 2 2 1 0 0
Sample Output
4 Oh,it's impossible~!!
Hint
第一组数据的说明:
一共以下四种方法:
操作开关1
操作开关2
操作开关3
操作开关1、2、3 (不记顺序)
一共以下四种方法:
操作开关1
操作开关2
操作开关3
操作开关1、2、3 (不记顺序)
题意概括:
如题。
解题思路:
根据开关之间的关系可以构造一个0,1矩阵,然后通过求解这个矩阵,相加模2(即异或操作),求解线性方程组。
一个自由元即产生 2 种可能性,假设最后解的方程组存在ans个自由元 ,方案数就是 2 的 ans 次幂;
如何构造这样一个0,1增广矩阵呢?
设方程个数为 equ 个, 未知数个数为 var 个,我们最终构造出来的是一个 equ*(var+1)的0,1矩阵。
最后一列 a [ i ][ var + 1 ] 很容易 就是 初始状态 st [ i ] ^ 最终状态 ed [ i ];
而前面的系数矩阵呢?
其实是一个 N*N 的矩阵,可以把开关之间的关系理解成图的边,这个系数矩阵就是这个图的邻接矩阵。
构造出了增广矩阵,接下来的就是交给高斯消元去求解这个方程组了。
Ac code:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 #include <cmath> 6 #include <map> 7 #define LL long long 8 #define INF 0x3f3f3f3f 9 using namespace std; 10 const int MAXN = 35; 11 int N, cnt; 12 13 int a[MAXN][MAXN]; //增广矩阵 14 int x[MAXN]; //解集 15 bool freeX[MAXN]; //标记自由元 16 int free_num; //自由元个数 17 int st[MAXN]; //记录初始状态 18 int ed[MAXN]; //记录最终状态 19 20 int Gauss(int equ, int var) 21 { 22 int maxRow, col, k; 23 free_num = 0; 24 for(k = 0, col = 0; k < equ && col < var; k++, col++){ 25 maxRow = k; 26 for(int i = k+1; i < equ; i++){ //寻找当前列绝对值最大的一行 27 if(abs(a[i][col]) > abs(a[maxRow][col])){ 28 maxRow = i; 29 } 30 } 31 if(a[maxRow][col] == 0){ //表示当前列绝对值最大的已经是0了,说明该列下面的全部都是0 32 k--; 33 freeX[free_num++] = col; 34 continue; 35 } 36 if(maxRow != k){ //绝对值最大的一行与当前行交换 37 for(int j = col; j < var+1; j++){ 38 swap(a[k][j] , a[maxRow][j]); 39 } 40 } 41 for(int i = k+1; i < equ; i++){ //以绝对值最大的一行为标准对其他行进行消元 42 if(a[i][col] != 0){ 43 for(int j = col; j < var+1; j++){ 44 a[i][j] ^= a[k][j]; 45 } 46 } 47 } 48 } 49 for(int i = k; i < equ; i++) //判断是否无解 50 if(a[i][col] != 0) 51 return -1; 52 53 if(k < var){ 54 return var-k; //自由元的个数 55 } 56 //唯一解,回代 57 for(int i = var-1; i >= 0; i--){ 58 x[i] = a[i][var]; 59 for(int j = i+1; j < var; j++){ 60 x[i] ^= (a[i][j] && x[j]); 61 } 62 } 63 return 0; 64 } 65 66 int main() 67 { 68 int T_case; 69 int u, v; 70 scanf("%d", &T_case); 71 while(T_case--){ 72 scanf("%d", &N); 73 for(int i = 0; i < N; i++){ 74 scanf("%d", &st[i]); 75 } 76 for(int i = 0; i < N; i++){ 77 scanf("%d", &ed[i]); 78 } 79 memset(a, 0, sizeof(a)); 80 memset(x, 0, sizeof(x)); 81 //构造增广矩阵 82 for(int i = 0; i < N; i++){ //本开关肯定对本开关有影响 83 a[i][i] = 1; 84 } 85 86 while(~scanf("%d%d", &u, &v) && (u+v)){ //构造对除自身外开关的影响,自身是系数也是未知量 87 a[v-1][u-1] = 1; 88 } 89 for(int i = 0; i < N; i++){ //结果 90 a[i][N] = st[i]^ed[i]; //这里异或相当于对2取模运算 91 } 92 int ans = Gauss(N, N); 93 if(ans == -1) printf("Oh,it's impossible~!! "); //无解 94 else printf("%d ", 1<<ans); //2的ans次方,因为有ans个自由元 95 } 96 return 0; 97 }