开关问题(POJ-1830)
Problem Description
有N個相同的開關(guān),每個開關(guān)都與某些開關(guān)有著聯(lián)系,每當(dāng)你打開或者關(guān)閉某個開關(guān)的時候,其他的與此開關(guān)相關(guān)聯(lián)的開關(guān)也會相應(yīng)地發(fā)生變化,即這些相聯(lián)系的開關(guān)的狀態(tài)如果原來為開就變?yōu)殛P(guān),如果為關(guān)就變?yōu)殚_。你的目標(biāo)是經(jīng)過若干次開關(guān)操作后使得最后N個開關(guān)達(dá)到一個特定的狀態(tài)。對于任意一個開關(guān),最多只能進(jìn)行一次開關(guān)操作。你的任務(wù)是,計算有多少種可以達(dá)到指定狀態(tài)的方法。(不計開關(guān)操作的順序)
Input
輸入第一行有一個數(shù)K,表示以下有K組測試數(shù)據(jù)。?
每組測試數(shù)據(jù)的格式如下:?
第一行 一個數(shù)N(0 < N < 29)?
第二行 N個0或者1的數(shù),表示開始時N個開關(guān)狀態(tài)。?
第三行 N個0或者1的數(shù),表示操作結(jié)束后N個開關(guān)的狀態(tài)。?
接下來 每行兩個數(shù)I J,表示如果操作第 I 個開關(guān),第J個開關(guān)的狀態(tài)也會變化。每組數(shù)據(jù)以 0 0 結(jié)束。?
Output
如果有可行方法,輸出總數(shù),否則輸出“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~!!
思路:
根據(jù)題目可知是一個高斯消元求異或方程組的題,要求總共的方案數(shù),那么也是要先求出自由元的個數(shù) freeNum,由于每個自由元有 1、0 兩種狀態(tài),那么最后結(jié)果就是?1<<freeNum
n 個燈泡,構(gòu)造 n 個方程,根據(jù)給出的開始狀態(tài)與結(jié)束狀態(tài)可知,如果一個燈開始狀態(tài)與結(jié)束狀態(tài)不同,那么這個燈肯定是被按過,反之則沒有被按過,那么每個方程的解 a[i][n] 就是每個燈開始狀態(tài)與結(jié)束狀態(tài)的異或
接著看左邊的系數(shù)矩陣部分,對于每個開關(guān)來說,每次按開關(guān),開關(guān)自身會發(fā)生變換,那么 a[i][i]=1,即第 i 個開關(guān)受第 i 個開關(guān)影響,然后再看給出兩個關(guān)聯(lián)的開關(guān) t1、t2,由于關(guān)聯(lián)的兩個開關(guān)相互影響,按下 t1 之后 t2 隨之改變,那么就有 a[t2][t1]=1,即第 t2 個開關(guān)受第 t1 個開關(guān)影響
最后進(jìn)行高斯消元,尋找自由元的個數(shù)然后計算 1<<freeNum 即可
Source Program
#include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<utility> #include<stack> #include<queue> #include<vector> #include<set> #include<map> #define E 1e-9 #define PI acos(-1.0) #define INF 0x3f3f3f3f #define LL long long const int MOD=6; const int N=1000+5; const int dx[]= {-1,1,0,0}; const int dy[]= {0,0,-1,1}; using namespace std;int a[N][N];//增廣矩陣 int x[N];//解集 int freeX[N];//自由變元 int Gauss(int equ,int var){//返回自由變元個數(shù)/*初始化*/for(int i=0;i<=var;i++){x[i]=0;freeX[i]=0;}/*轉(zhuǎn)換為階梯陣*/int col=0;//當(dāng)前處理的列int num=0;//自由變元的序號int k;//當(dāng)前處理的行for(k=0;k<equ&&col<var;k++,col++){//枚舉當(dāng)前處理的行int maxRow=k;//當(dāng)前列絕對值最大的行for(int i=k+1;i<equ;i++){//尋找當(dāng)前列絕對值最大的行if(abs(a[i][col])>abs(a[maxRow][col]))maxRow=i;}if(maxRow!=k){//與第k行交換for(int j=k;j<var+1;j++)swap(a[k][j],a[maxRow][j]);}if(a[k][col]==0){//col列第k行以下全是0,處理當(dāng)前行的下一列freeX[num++]=col;//記錄自由變元k--;continue;}for(int i=k+1;i<equ;i++){if(a[i][col]!=0){for(int j=col;j<var+1;j++){//對于下面出現(xiàn)該列中有1的行,需要把1消掉a[i][j]^=a[k][j];}}}}/*求解*///無解:化簡的增廣陣中存在(0,0,...,a)這樣的行,且a!=0for(int i=k;i<equ;i++)if(a[i][col]!=0)return -1;return var-k;//自由變元有var-k個 } int start[N];//開始狀態(tài) int endd[N];//結(jié)束狀態(tài) int main(){int t;scanf("%d",&t);while(t--){int n;scanf("%d",&n);for(int i=0;i<n;i++)//開始狀態(tài)scanf("%d",&start[i]);for(int i=0;i<n;i++)//最終狀態(tài)scanf("%d",&endd[i]);memset(a,0,sizeof(a));for(int i=0;i<n;i++)//每個方程的解a[i][n]=start[i]^endd[i];for(int i=0;i<n;i++)//第i個開關(guān)受第i個開關(guān)影響a[i][i]=1;int t1,t2;while(scanf("%d%d",&t1,&t2)!=EOF&&(t1+t2)){//a[t1-1][t2-1]=1;a[t2-1][t1-1]=1;}int freeNum=Gauss(n,n);if(freeNum==-1)printf("Oh,it's impossible~!!\n");elseprintf("%d\n",1<<freeNum);}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的开关问题(POJ-1830)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信息学奥赛一本通(1054:三角形判断)
- 下一篇: 信息学奥赛一本通(1047:判断能否被3