ACWING208. 开关问题 POJ1830

有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。

你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。

对于任意一个开关,最多只能进行一次开关操作。

你的任务是,计算有多少种可以达到指定状态的方法。(不计开关操作的顺序)

输入格式
输入第一行有一个数K,表示以下有K组测试数据。

每组测试数据的格式如下:

第一行 一个数N(0 < N < 29)。

第二行 N个0或者1的数,表示开始时N个开关状态。

第三行 N个0或者1的数,表示操作结束后N个开关的状态。

接下来 每行两个数I J,表示如果操作第 I 个开关,第J个开关的状态也会变化。

每组数据以 0 0 结束。

输出格式
如果有可行方法,输出总数,否则输出Oh,it’s impossible~!! 。

输入样例:
2
3
0 0 0
1 1 1
1 2
1 3
2 1
2 3
3 1
3 2
0 0
3
0 0 0
1 0 1
1 2
2 1
0 0
输出样例:
4
Oh,it’s impossible~!!

思路:
一开始一直没搞懂为啥可以高斯消元,原因是本题可以列异或方程,而且这个方程也符合初等行运算,只是运算方式变成了异或。

a i j aij 的意思是按下第j个数字是否影响第i个数字,故每一位只有0和1
a数组可以用状压的方式存储(或者bitset),不用开两维。
特别的 a i i = 1 , a i 0 = s t a r t i X O R e n d i aii = 1, ai0 = starti XOR endi

可以列出n个这样的方程:
x 1 a 11 X O R x 2 a 12 X O R x 3 a 13.. X O R x n a 1 n = s t a r t 1 X O R e n d 1 x1a11 XOR x2a12 XOR x3a13..XOR xna1n=start1 XOR end1

我们要求的是方案数,

高斯消元过程中出现a[i] = 0,
也就是系数与最右边的数为0的情况时,意味着本行以下的变元都为自由元了,取0取1都无妨,答案就是2cnt,cnt代表包括当前行的剩余行数

如果出现a[i] = 1的情况,
意味着出现了0 = 1的情况,也就是系数为0,方程最右边为1。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int a[1005];

int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n;scanf("%d",&n);
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i = 1;i <= n;i++)
        {
            int x;scanf("%d",&x);
            a[i] ^= x;
            a[i] |= 1 << i;
        }
        
        int x,y;
        while(~scanf("%d%d",&x,&y) && x && y)
        {
            a[y] |= 1 << x;
        }
        int 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] == 0){ans = 1 << (n - i + 1);break;}//如果a[i] == 0,说明之后的元系数都为0,已经算出不来了成为了自由元
            if(a[i] == 1){ans = 0;break;}//0 == 1,无解了。
            
            for(int k = n;k >= 1;k--)
            {
                if(a[i] & (1 << k))
                {
                    for(int j = 1;j <= n;j++)
                    {
                        if(i != j && a[j] & (1 << k))a[j] ^= a[i];//消掉其他方程的a[i]最高位(第k位)
                    }
                    break;
                }
            }
        }
        if(ans == 0)printf("Oh,it's impossible~!!\n");
        else printf("%d\n",ans);
    }
    return 0;
}

发布了697 篇原创文章 · 获赞 22 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/104287678