POJ1830 开关问题

中文题目,就不说题目大意了
解题思路:
由于对于每一个开关最多改变一次,那么对于每一个开关,只有改变与不改变两种操作,设改变操作为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 ,那么 nj=1aijxj 就表示开关i被改变的次数。显然,改变次数为奇数,则说明改变初始状态,反之不变。若用向量 b⃗ T=(b1,b2,,bn) 表示初始状态与目标状态之间的关系, bi 表示开关 i 的目标与初始状态是否一致,1表示不一致, 0 表示一致,则方程nj=1aijxjbi(mod2)表示将开关 i 改变为目标状态。由于模运算的性质,我们可以先对线性方程组Ax=b进行初等行变换再对两边取模。由于只需要求可行方案数(即方程解的个数),而并不关心解本身,我们仅仅求得线性方程组系数矩阵与增广矩阵的秩即可。若 r(A)<r(A,b) 则方程无解(无可行方案),若 r(A)=r(A,b)=n 则方程有唯一解(有一种方案),若 r(A)=r(A,b)<n ,由于解向量中自由变元仅有两种取值 (01) ,可行方案数为 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、付费专栏及课程。

余额充值