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