poj1830开关问题

题意:有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。

思路:令a[i][j]为不同灯泡的对应关系,默认a[i][i]=1,若a[i][j]=1,则说明第i个灯泡变化时第j个灯泡也改变。可以列出异或方程组\left\{\begin{matrix} a_{1,1}x_{1} xor a_{1,2}x_{2} xor ... xor a_{1,n}x_{n} = src_{1} xor dst_{1}\\ a_{2,1}x_{1} xor a_{2,2}x_{2} xor ... xor a_{2,n}x_{n} = src_{1} xor dst_{1} \\ ......\\ a_{n,1}x_{1} xor a_{n,2}x_{2} xor ... xor a_{n,n}x_{n} = src_{1} xor dst_{1} \end{matrix}\right.

xi即为操作,1是改变该灯泡,0是不变。本题是求解的个数。由线性代数知识可以知道:

线性方程组的系数矩阵为A,增广矩阵为(A,B),则

当r(A)=r(A,B)=n时,有唯一解。

当r(A)=r(A,B)<n时,有2^(n-r)组解(x只有1,0两种情况,所以是2^(n-r)个解吧,我猜的

当r(A)!=r(A,B)时,无解。

今天下午poj崩了,代码交不上去,下面代码不一定对

#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
int t, n, x, y, a[100][100], s[100], e[100], b[100];
int cmp(int i, int j) {
    int ai = 0, aj = 0;
    for(int k = n; k > 0; k--) {
        ai += (a[i][k] << (n - k));
        aj += (a[j][k] << (n - k));
    }
    return aj > ai;
}
int main() {
    cin >> t;
    while(t--) {
        cin >> n;
        memset(a, 0, sizeof a);
        for(int i = 1; i <= n; i++)
            cin >> s[i], a[i][i] = 1;
        for(int i = 1; i <= n; i++)
            cin >> e[i], b[i] = s[i] ^ e[i];
        while(cin >> x >> y && (x + y)) {
            a[y][x] = 1;
        }
        int ans = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = i + 1; j <= n; j++)
                if(cmp(i, j)) {
                    for(int k = 1; k <= n; k++)
                        swap(a[i][k], a[j][k]);
                    swap(b[i], b[j]);
                }
            int f = 0;
            for(int j = 1; j <= n; j++) {
                if(a[i][j])
                    f = 1;
            }
            if(f == 0 && b[i] == 1) {
                ans = 0;
                break;
            }
            if(f == 0 && b[i] == 0) {
                ans = (1 << (n - i + 1));
                break;
            }
            for(int k = 1; k <= n; k++)
                if(a[i][k]) {
                    for(int j = 1; j <= n; j++)
                        if(i != j && a[j][k]) {
                            for(int s = 1; s <= n; s++)
                                a[j][s] ^= a[i][s];
                            b[j] ^= b[i];
                        }
                    break;
                }
        }
        if(ans == 0)
            puts("Oh,it's impossible~!!");
        else
            printf("%d\n", ans);
    }
    return 0;
}

由于只有01两个状态并且只有29的数组,所以可以进行状态压缩,可以用int的每一个二进制位代表一个状态。

#include<cstdio>
#include<algorithm>
using namespace std;
int t, n, a[41], ans, x, y;
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            scanf("%d", a + i);
        for(int j, i = 1; i <= n; i++) {
            scanf("%d", &j);
            a[i] ^= j;
            a[i] |= (1 << i);//a[i][i]=1;
        }
        while(~scanf("%d%d", &x, &y) && (x + y)) {
            a[y] |= (1 << x);//a[y][x]=1
        }
        ans = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = i + 1; j <= n; j++)
                if(a[j] > a[i])
                    swap(a[i], a[j]);
            if(!a[i]) {
                ans = 1 << (n - i + 1);//只有i-1个主元 
                break;
            }
            if(a[i] == 1) {//只有常数项不是0 
                ans = 0;
                break;
            }
            for(int k = n; k; k--)
                if(a[i] >> k & 1) {
                    for(int j = 1; j <= n; j++)
                        if(i != j && (a[j] >> k & 1))
                            a[j] ^= a[i];
                    break;
                }
        }
        if(ans == 0)
            puts("Oh,it's impossible~!!");
        else
            printf("%d\n", ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值