开关问题(高斯消元)

拟墨画扇 提交于 2020-02-25 20:46:10

开关问题(ACwing)

Description

有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~!! 。

 

Solution

每个开关只能操作一次,求一个目标状态

可设数组x,x[i]表示是(取值为 1 )否(取值为 0 )对第 i 个开关操作,任务是求出x[i]

参照开关之间的关系,我们可以对每个开关列一个方程,一共 n 个方程

第 i 个方程等号右边的第 j 个单项式为 a[i][j]*x[j] ,

a[i][j]表示操作开关 j 是(取值为1)否(取值为0)会影响开关 i 的状态

等号右边为第 i 个开关起始状态^目标状态

于是可以用高斯消元求这个线性方程组的解(lyd大佬码风太强啦)

 

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
int a[30],T,n,x,y,ans;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            a[i]^=x,a[i]|=(1<<i);
        }
        while(~scanf("%d%d",&x,&y) && x && y)
            a[y]|=(1<<x);
        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]==1)
            {
                ans=0;
                break;
            }
            if(a[i]==0)
            {
                ans=1<<(n-i+1);
                break;
            }
            for(int k=1;k<=n;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;
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!