http://poj.org/problem?id=1830
题意:把开关从初始状态变到目标状态有多少种移动方式。
思路:用线性代数的解方程组求解
图中 ai 是矩阵的列向量, 表示变换第 i 个开关会影响到那些开关。
如上图a1 = {1 0 1 0} 表示变换第一个开关 则第一个开关和第三个开关都会变换,图中的 xi 取值为 1 或者 0,分别表示第 i 个开关变换或者不变换。
我们的初始状态定义为 bs = {0 0 0 0}, 目标状态定义为 bt = {1 1 1 1},
很容易看出我们从起始状态变成目标状态有多少种变换方式其实就是看 x1, x2, x3, x4有多少种取值方式,当然x只能取0/1(因为同一个开关按两次核不按是一样的,按三次和按一次也是一样的)。
然后题目就变成了解方程 x1a1 + x2a2 +x3a3 = bt-bs。下面就是解这个线性方程组
对增广矩阵[m (bt-bs)]做初等行变换,化成阶梯形(高斯消元法),如果存在[0,0,…,0,1]的行,就是无解;如果存在 x 行[0,0,…,0,0],就意味着有 x 个自由变量,因为这里的变量只取0或者1,所以有2x个解,如果不存在[0,0,…,0,0]也不存在[0,0,…,0,1],则A为满秩矩阵,则方程组有唯一解
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n;
int temp[55];
int mapn[55][55];
int Gauss(int n, int m)
{
for(int i = 0, k = 0; i < n && k < m; i++, k++) //i代表行k代表消到多少列了
{
int maxn = i;
for(int h = i+1; h < n; h++) //找出第k列不是0的行
{
if(mapn[h][k] > mapn[maxn][k])
{
maxn = h;
}
}
if(maxn != i)
{
for(int h = k; h < m; h++) //因为第i行第k列是0,所以交换成不是0的行
{
swap(mapn[i][h], mapn[maxn][h]);
}
}
if(mapn[i][k] == 0) //第k列全部都是0
{
i--;
continue;
}
for(int h = i+1; h < n; h++) //通过第i行消除i~n-1行的第k列元素
{
if(mapn[h][k] == 0) //不需要消
continue;
for(int l = k; l < m; l++) //消消消。。
{
mapn[h][l] = mapn[h][l] ^ mapn[i][l];
}
}
}
return 0;
}
int solve(int n, int m)
{
for(int i = 0; i < n; i++) //遍历每一行
{
int k;
for(k = 0; k < m; k++) //遍历第i列
{
if(mapn[i][k] != 0) //不是0就退出
{
if(k == m-1) //无解
return 0;
break;
}
}
if(k == m) //表示第i行全部是0
return 1<<(n - i);
}
return 1;
}
int main()
{
int Test;
cin>>Test;
while(Test--)
{
cin>>n;
int x, y;
memset(temp, 0, sizeof(temp));
memset(mapn, 0, sizeof(mapn));
for(int i = 0; i < n; i++)
{
cin>>temp[i]; //输入起始状态
mapn[i][i] = 1; //变换自己 自己肯定会变换(好别扭。。。)
}
for(int i = 0; i < n; i++)
{
cin>>x;
mapn[i][n] = x ^ temp[i]; //相当于目标状态减去起始状态
}
while(true)
{
cin>>x>>y;
if(x == 0 && y == 0)
break;
mapn[y-1][x-1] = 1; //从第0行0列开始
}
Gauss(n, n+1); //高斯消元
int solv = solve(n, n+1); //判断变元
if(!solv){
cout<<"Oh,it's impossible~!!"<<endl;
}
else{
cout<<solv<<endl;
}
}
return 0;
}