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
sample output2
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
hint4
oh,it's impossible~!!
第一組資料的說明:
一共以下四種方法:
操作開關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...