[POJ 1830] 开关问题 高斯消元

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

achonor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值