開關問題 POJ 1830 高斯消元

2021-08-18 13:01:37 字數 3758 閱讀 1576

poj - 1830 

有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

30 0 0

1 1 1

1 21 3

2 12 3

3 13 2

0 03

0 0 0

1 0 1

1 22 1

0 0

sample output

4

oh,it's impossible~!!

hint

第一組資料的說明: 

一共以下四種方法: 

操作開關1 

操作開關2 

操作開關3 

操作開關1、2、3 (不記順序) 

思路:為了練習高斯消元找來的題目,當然是用高斯消元了……

解:既然要解方程,那麼首先我們要清楚我們要求的是什麼(雖然看起來像廢話,但是我一開始真的想了半天

顯然,我們需要求每個開關是否開啟。對於開關來說,只有開與不開兩種情況,可以用0 1表示

輸入有n個開關,所以我們需要有n個方程。然後我們需要建立方程

怎麼把開關與燈的狀態聯絡起來?

對於燈1來說,一共有n個開關,我們可以把能改變燈1狀態的開關設成1,不能改變的設成0

顯然這裡要乙個n*n的二維陣列

對於方程的結果,可以根據燈1的開始狀態和結束狀態設定,相同為0,不同為1

那麼我們可以得到以下方程: use[1]表示開關1 簡寫成u[1]   relate[1][2]表示開關1對燈2的影響 簡寫成r[1][2]

u[1]*r[1][1]+u[2]*r[2][1]+u[3]*r[3][1]+u[4]*r[4][1]……+u[n]*r[n][1] = start[1]^end[1]  //start,end 表示初始狀態,結尾狀態

橋豆麻袋,中間這個+是怎麼回事,這樣方程能成立嗎?

經過我的思索,我覺得中間的關係應該是^不是+,雖然寫成矩陣的時候看不出來,但是這個一定要一開始就明確

正確的方程應該是:

u[1]*r[1][1]^u[2]*r[2][1]^u[3]*r[3][1]^u[4]*r[4][1]……^u[n]*r[n][1] = start[1]^end[1] 

列出n個以後再把要求的u[1]到u[n]提出來,就可以得到矩陣

r[1][1]  r[2][1]  r[3][1]  r[4][1]  ……  r[n][1]   start[1]^end[1] 

r[1][2]  r[2][2]  r[3][2]  r[4][2]  ……  r[n][2]   start[2]^end[2] 

r[1][3]  r[2][3]  r[3][3]  r[4][3]  ……  r[n][3]   start[3]^end[3] 

r[1][n]  r[2][n]  r[3][n]  r[4][n]  ……  r[n][n]  start[n]^end[n] 

這時候會發現,r[i][j],和正常的有點不一樣,所以等會輸入的時候需要處理一下(行列互換

到這裡,前期的準備工作算是ok了,下面開始解方程

所謂高斯消元法,就和我們平時解方程一樣,通過不斷地帶入消除未知數,拿到乙個變數的解以後再帶回到其他方程,得到其它變數的解

在矩陣裡,我們可以把矩陣轉化為上三角的形式,然後通過原矩陣與增高矩陣的秩,來判斷有沒有解(如果有唯一解的話,可以帶回去把解求出來,當然這題不用求

關於秩和解的關係,這裡稍微列一下,因為我的線代其實也忘的差不多……

r(a) = r(a,b)           有解

r(a) = r(a,b) = n     有唯一解     (n是未知量的個數,即a的列數)  

r(a) = r(a,b) < n     有無窮多解

另外r(a,b) > r(a) 時,無解

需要注意的是,本題在r(a)=r(a,b)從矩陣上看,有(n-r(a))行為全0,意味著它們對應的use[i] 可以0,1間任取

顯然有一行的話就是2的一次種情況,兩行就是2的2次,三行就是2的3次。**表示就是(1<

表面上看,到這裡就結束了……其實並沒有,還有最後乙個問題

正常情況下,消元會從(1,1)開始到(n,n)一格格下去,如果第(n,n)為0呢,就往下搜乙個非0的,再整行互換(在其他題目中,為了精度可以找絕對值最大的元素,然後整行互換

這樣會有什麼問題呢?我們就不能從最後面直接開始數,而要轉為全部遍歷數一遍 ,但是數一次也沒問題,時間複雜度不高

問題出在數的方式,一開始我是原矩陣的秩和增廣矩陣的秩分開數,然後不斷的wa,後來才發現

如果某一行,原矩陣全為0並且對應的增廣矩陣為1,那麼一定無解。因為這時候增廣矩陣的秩肯定大於原矩陣的秩了

下面的**是避開遍歷relate,通過id記錄自由元的方式,比較快

**:

#include#include#includeusing namespace std;

int start[33];

int end[33];

int relate[33][33];

int guass(int m)

for(j=id+1;j<=m;j++)

} for(i=id;i<=m;i++)//m-id是已確定的自由元的個數,若[id,m]之間有relate[i][m+1]>0

if(relate[i][m+1]) return -1;//則證明增廣矩陣的秩大於原矩陣的秩,方程無解

return r1;

}int main()

for(int i=1;i<=m;i++)

cin>>end[i];

int a,b;

while(cin>>a>>b)

for(int i=1;i<=m;i++)

relate[i][m+1] = end[i]^start[i];

int tmp=guass(m);

if(tmp == -1) cout<

如果遍歷數的話,是這個函式

int guass(int m)

for(j=i+1;j<=m;j++)

} for(i=1;i<=m;i++)

r2+=flag;

if(flag && relate[i][j]) return -1;

if(relate[i][j]) r1++;

} if(r1>(m-r2)) return -1;

else return r2;

}

POJ 1830 開關問題 高斯消元

開關問題 time limit 1000ms memory limit 30000k total submissions 3390 accepted 1143 description 有n個相同的開關,每個開關都與某些開關有著聯絡,每當你開啟或者關閉某個開關的時候,其他的與此開關相關聯的開關也會相應...

poj 1830 開關問題 高斯消元

題意是 給一些開關的初始狀態 0 或1 在給出終止狀態,在給出相關的變化規則,規則 x 變化 則 y 也變 x y 讀入。輸出有多少種開關的撥動情況,使初始狀態變成終止狀態。此問題 很容易轉化成 高斯消元 解 異或方程組。t 方程組的自由化的個數,則結果就是 2 t include include ...

poj 1830 開關問題(高斯消元)

終止狀態是從初始狀態由開關組合影響而形成的,那麼就有乙個等式使得初始狀態可以到達終止狀態,例如a,b,c三個開關 e a xa mp a a xb mp a b xc map a c s a e b xa mp b a xb mp b b xc map b c s b e c xa mp c a x...