POJ 1830 開關問題 【01矩陣 高斯消元】
阿新 • • 發佈:2018-11-16
任意門:http://poj.org/problem?id=1830
開關問題
Time Limit: 1000MS |
|
Memory Limit: 30000K |
Total Submissions: 10742 |
|
Accepted: 4314 |
Description
有N個相同的開關,每個開關都與某些開關有著聯絡,每當你開啟或者關閉某個開關的時候,其他的與此開關相關聯的開關也會相應地發生變化,即這些相聯絡的開關的狀態如果原來為開就變為關,如果為關就變為開。你的目標是經過若干次開關操作後使得最後N個開關達到一個特定的狀態。對於任意一個開關,最多隻能進行一次開關操作。你的任務是,計算有多少種可以達到指定狀態的方法。(不計開關操作的順序)Input
每組測試資料的格式如下:
第一行 一個數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