POJ1830高消
開關問題
Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 4088 | Accepted: 1433 |
Description
有N個相同的開關,每個開關都與某些開關有著聯係,每當你打開或者關閉某個開關的時候,其他的與此開關相關聯的開關也會相應地發生變化,即這些相聯係的開關的狀態如果原來為開就變為關,如果為關就變為開。你的目標是經過若幹次開關操作後使得最後N個開關達到一個特定的狀態。對於任意一個開關,最多隻能進行一次開關操作。你的任務是,計算有多少種可以達到指定狀態的方法。(不計開關操作的順序)
Input
輸入第一行有一個數K,表示以下有K組測試數據。
每組測試數據的格式如下:
第一行 一個數N(0 < N < 29)
第二行 N個0或者1的數,表示開始時N個開關狀態。
第三行 N個0或者1的數,表示操作結束後N個開關的狀態。
接下來 每行兩個數I J,表示如果操作第 I 個開關,第J個開關的狀態也會變化。每組數據以 0 0 結束。
每組測試數據的格式如下:
第一行 一個數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 (不記順序)
一共以下四種方法:
操作開關1
操作開關2
操作開關3
操作開關1、2、3 (不記順序)
Source
關係要注意逆向推出相關性
即代碼
while(cin>>s1>>s2&&s1&&s2)
a[s2-1][s1-1]=1;
而不是
while(cin>>s1>>s2&&s1&&s2)
a[s1-1][s2-1]=1;
按照題意來即可
#include <iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; const int maxn=230; int a[maxn][maxn+1],x[maxn];//a 是係數矩陣和增廣矩陣,x 是最後存放的解 // a[][maxn]中存放的是方程右麵的值(bi) int equ,var;//equ 是係數陣的行數,var 是係數矩陣的列數(變量的個數) int free_num,ans=100000000; int abs1(int num) //取絕對值 { if (num>=0) return num; else return -1*num; } void Debug(void) { int i, j; for (i = 0; i < equ; i++) { for (j = 0; j < var + 1; j++) { cout << a[i][j] << " "; } cout << endl; } cout << endl; } //調試輸出,看消元後的矩陣值,提交時,就不用了 inline int gcd(int a, int b) //最大公約數 { int t; while (b != 0) { t = b; b = a % b; a = t; } return a; } inline int lcm(int a, int b) //最小公倍數 { return a * b / gcd(a, b); } int dfs(int p) //枚舉自由解,隻能取0-1,枚舉完就回帶,找到最小的 { if (p<=free_num-1) //深入到了主對角線元素非0 的行了 { //下麵就是回帶的代碼啊 for(int i = free_num-1; i >= 0; --i) { int tmp = a[i][var] % 2; for(int j = i+1; j < var; ++j) //x[i]取決於x[i+1]--x[var]啊,所以後麵的解對前麵的解有影 //響。 if(a[i][j] != 0) tmp = ( tmp - (a[i][j]*x[j])%2 + 2 ) % 2; x[i] = (tmp/a[i][i]) % 2; //上麵的正常解 } //回帶完成了 //計算解元素為1 的個數; int sum=0; for(int i=0; i<var; i++) sum+=x[i]; if (ans>sum) ans=sum; return 0; } x[p]=0; dfs(p-1); x[p]=1; dfs(p-1); } void swap(int &a,int &b) { int temp=a; //交換 2 個數 a=b; b=temp; } long long Gauss() { int k,col = 0; //當前處理的列 for(k = 0; k < equ && col < var; ++k,++col) { int max_r = k; for(int i = k+1; i < equ; ++i) if(a[i][col] > a[max_r][col]) max_r = i; if(max_r != k) { for(int i = k; i < var + 1; ++i) swap(a[k][i],a[max_r][i]); } if(a[k][col] == 0) { k--; continue; } for(int i = k+1; i < equ; ++i) { if(a[i][col] != 0) { int LCM = lcm(a[i][col],a[k][col]); int ta = LCM/a[i][col], tb = LCM/a[k][col]; if(a[i][col]*a[k][col] < 0) tb = -tb; for(int j = col; j < var + 1; ++j) a[i][j] = ( (a[i][j]*ta)%2 - (a[k][j]*tb)%2 + 2 ) % 2; // 0 和 1 兩種狀態 } } } //a[i][j]隻有 //上述代碼是消元的過程,行消元完成 //解下來 2 行,判斷是否無解 //注意 K 的值,k 代表係數矩陣值都為 0 的那些行的第 1 行 for(int i = k; i < equ; ++i) if(a[i][col] != 0) return -1; //Debug(); //唯一解或者無窮解,k<=var //var-k==0 唯一解;var-k>0 無窮多解,自由解的個數=var-k //能執行到這,說明肯定有解了,無非是 1 個和無窮解的問題。 //下麵這幾行很重要,保證秩內每行主元非 0,且按對角線順序排列,就是檢查列 for(int i = 0; i <equ; ++i)//每一行主元素化為非零 if(!a[i][i]) { int j; for(j = i+1; j<var; ++j) if(a[i][j]) break; if(j == var) break; for(int k = 0; k < equ; ++k) swap(a[k][i],a[k][j]); } // ----處理保證對角線主元非 0 且順序,檢查列完成 free_num=k; if (var-k>0) { return (__int64)pow(double(2),double(var-k)); } if(var-k<0) return -1; // 無解返回 -1 if (var-k==0)//唯一解時 { return 1; } } int main() { int n,t,m1[30],m2[30],s1,s2; cin>>t; while(t--) { memset(a,0,sizeof(a)); memset(x,0,sizeof(x)); cin>>n; var=equ=n; for(int i=0; i<n ; i++) cin>>m1[i]; for(int j=0; j<n; j++) { cin>>m2[j]; if(m2[j]!=m1[j]) a[j][n]=1; a[j][j]=1; } while(cin>>s1>>s2&&s1&&s2) a[s2-1][s1-1]=1; // Debug(); long long j1=Gauss(); // Debug(); if(j1==-1) cout<<"Oh,it's impossible~!!"<<endl; else cout<<j1<<endl; } return 0; }
最後更新:2017-04-02 15:28:25