POJ 1830 开关问题(高斯消元法)

解题思路:
由于对于每一个开关最多改变一次,那么对于每一个开关,只有改变与不改变两种操作,设改变操作为1,不改变操作为0,那么对开关的操作可以用一个n维向量x⃗ T=(x1,x2,⋯,xn),其中xi=0或者1。
我们需要知道初始状态经过某次操作之后的状态,并拿它与目标状态比较。我们知道,对于开关i,除了对i的操作xi会影响其状态,与其相关联的开关j的操作xj也会影响其状态。显然,只有当开关j影响开关i并且xj=1(即改变开关j),才会使开关i改变。令aij表示开关j是否影响开关i,影响为1,不影响为0,则aijxj表示开关j是否改变开关i,改变为1,不改变为0。若定义aii=1,那么∑n,j=1(aijxj)就表示开关i被改变的次数。显然,改变次数为奇数,则说明改变初始状态,反之不变。若用向量b⃗ T=(b1,b2,⋯,bn)表示初始状态与目标状态之间的关系,bi表示开关i的目标与初始状态是否一致,1表示不一致,0表示一致,则方程∑n,j=1(aijxj)≡bi(mod2)表示将开关i改变为目标状态。由于模运算的性质,我们可以先对线性方程组Ax=b进行初等行变换再对两边取模。由于只需要求可行方案数(即方程解的个数),而并不关心解本身,我们仅仅求得线性方程组系数矩阵与增广矩阵的秩即可。若r(A) < r(A,b)则方程无解(无可行方案),若r(A)=r(A,b)=n则方程有唯一解(有一种方案),若r(A)=r(A,b) < n,由于解向量中自由变元仅有两种取值(0或1),可行方案数为2r(A,b)−r(A)。

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;

int ab[50][50], st[50], en[50];

inline int gauss(int n)
{
    int i = 0, j = 0, k;
    while (j < n)
    {
        int id = i;
        for (k = i + 1; k < n; k++)
            if (abs(ab[k][j]) > abs(ab[id][j]))
                id = k;
        if (id != i)
        {
            for (k = j; k <= n; k++)
                swap(ab[i][k], ab[id][k]);
        }
        if (ab[i][j] == 0) { j++; continue; }
        for (k = i + 1; k < n; k++)
        {
            if (ab[k][j] == 0) continue;
            for (int l = j; l <= n; l++)
                ab[k][l] = ab[k][l] ^ ab[i][l];
        }
        i++, j++;
    }
    for (int k = i; k < n; k++) 
        if (ab[k][n] != 0) return -1;
    return 1 << (n - i);
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) cin >> st[i];
        for (int i = 0; i < n; i++) cin >> en[i];
        memset(ab, 0, sizeof(ab));
        for (int i = 0; i < n; i++)
            ab[i][i] = 1, ab[i][n] = st[i] ^ en[i];
        int I, J;
        cin >> I >> J;
        while (I)
        {
            ab[J - 1][I - 1] = 1;
            cin >> I >> J;
        }
        int ans = gauss(n);
        if (ans == -1)  
            cout << "Oh,it's impossible~!!\n";
        else 
            cout << ans << endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值