bzoj 1923

高斯消元解异或方程组裸题

首先介绍一下异或方程组:异或方程组是形如$x_1$^$x_2$^...^$x_n$=$1(或0)$($x_i\in{0,1}$)的一组方程,我们的目的是求出x_i的值

乍一看,这好像并不好做,最暴力的方法是$O(2^n)$枚举

但是有了高斯消元,我们就可以不这么暴力了,而是可以借助高斯消元的思想来解了

举个例子:

现在我们有一个方程组长这样:

$x_1$^$x_3$=$0$

$x_1$^$x_2$=$0$

$x_1$^$x_2$^$x_3$=$1$

(当然,给出的例子是很简单的了)

但是我们可以借助这种思想:我们首先观察第一个方程:我们希望只有这一个方程中含有$x_1$(这也是高斯消元的思想),这样在其他变量已知的情况下我们就可以求出$x_1$了

那么根据异或的性质,我们用第一个方程逐个与下面含有$x_1$的方程异或,就可以消去下面所有的$x_1$!

于是做出来的结果就长这样:

$x_1$^$x_3$=$0$

$x_2$^$x_3$=$0$

$x_2$=$1$

接下来就是第二个方程:我们要把下面的$x_2$消掉,于是我们的方法同上

最后结果长这样:

$x_1$^$x_3$=$0$

$x_2$^$x_3$=$0$

$x_3$=$1$

最后一个方程,发现已经结束了,那么到这里结束,我们向上回代即可

而回代的原理,也很简单:

$a$^$b$=$c$可以推出$a$=$c$^$b$(两边异或b即得证)

所以我们算出$x_3$后将所有有$x_3$的等式两侧异或$x_3$即可,于是方程就变成了这样:

$x_1$=$1$

$x_2$=$1$

$x_3$=$1$

是不已经解出来了?

这就是高斯消元解异或方程组的过程及原理

 但是这里会出现一个问题:“自由元”问题

按道理,给出n个方程组应该是可以解出每个答案的,但是由于异或运算特殊的性质,有时n个方程是难以解出每个值的

为什么?

举个例子:

$x_1$^$x_2$=1

$x_2$^$x_3$=1

$x_1$^$x_3$=0

不难发现,这组方程本身解就不唯一(显然,$(1,0,1)$与$(0,1,0)$都是该方程组的解)

(如果去探索本质的原因:上面的异或方程在逻辑上等同于以下两个方程:$x_1==x_3$且$x_1!=x_2$,那么考虑取值的方式,不难发现解不是唯一的)

那么如果用正常的方式来解,最后会变成这种形式:

$x_1$^$x_2$=1

$x_2$^$x_3$=1

这也能看出来,因为丢失了一个方程啊!

这时我们就称$x_3$是一个自由元,也就是他的值既可以为0又可以为1,此时该异或方程组有很多组解!

对于自由元问题,目前仅有的办法就是dfs,枚举每个自由元的状态然后回代求解

但是对于这道题,我们没有必要这么做

由于对于解不唯一的情况,他只要求输出一句话,所以我们只需在解不唯一的情况下(也就是解了全部方程仍不能得到唯一解时)特判即可

接下来我们就可以研究如何输出最小方程数了

首先有个显而易见的事情:至少需要n个方程才能解出所有未知数

那我们就先拿出n个方程去解,看看是否存在自由元,如果存在的话就再加入一个,直到解出唯一解或用完所有方程为止

要记录所有自由元的位置

然后就结束了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
int n,m;
int a[2005][1005];
char ch[2005];
bool vis[2005];
int Gauss()
{
    int ret=0;
    for(int i=1;i<=n;i++)
    {
        int temp=i;
        while(!a[temp][i]&&temp<=n)temp++;
        if(temp==n+1){ret++;continue;}
        vis[i]=1;
        if(i!=temp)for(int j=i;j<=n+1;j++)swap(a[temp][j],a[i][j]);
        for(int j=i+1;j<=n;j++)
        {
            if(!vis[j]&&a[j][i])for(int k=i;k<=n+1;k++)a[j][k]^=a[i][k];
        }
    }
    return ret;
}
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=m;i++)
    {
        scanf("%s",ch+1);
        a[i][n+1]=read();
        for(int j=1;j<=n;j++)a[i][j]=ch[j]-'0';
    }
    int now=n;
    while(Gauss())
    {
        for(int i=1;i<=n;i++)
        {
            if(!vis[i])
            {
                now++;
                if(now>m)
                {
                    printf("Cannot Determine\n");
                    return 0;
                }
                swap(a[now],a[i]);
            }
        }
    }
    for(int i=n;i>=1;i--)for(int j=i-1;j>=1;j--)if(a[j][i])a[j][n+1]^=a[i][n+1];
    printf("%d\n",now);
    for(int i=1;i<=n;i++)
    {
        if(a[i][n+1])printf("?y7M#\n");
        else printf("Earth\n");
    }
    return 0;
}

 

posted @ 2019-05-05 19:21  lleozhang  Views(182)  Comments(0Edit  收藏  举报
levels of contents