1. 程式人生 > >POJ 1830 開關問題 【01矩陣 高斯消元】

POJ 1830 開關問題 【01矩陣 高斯消元】

任意門: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 結束。 

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 (不記順序) 

 

題意概括:

如題。

解題思路:

根據開關之間的關係可以構造一個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:

 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~!!\n");    //無解
94         else printf("%d\n", 1<<ans);                        //2的ans次方,因為有ans個自由元
95     }
96     return 0;
97 }
View Code