POJ1830(异或方程组的高斯消元)

对于式子(((A^B)^B)^B)……,即A对B进行连续异或时,可以发现有如下规律:

若B为1,式子的值会在每次异或之后取反,因为0^1=1,1^1=0,……

若B为0,式子的值会保持A值不变,因为0^0=0,1^0=1,……

因而这种性质可以利用在POJ1830这样的有关联的开关问题上:(每个开关只能改变一次)

假设开关1和开关2、3相关联,且开关1的初始状态为0,终态为1,。

用1表示该开关被改变过,0表示未被改变。

则开关1到达终态的情况可能有:①只改变开关1   ②改变开关1、2、3

则对应的异或式子为  ①((1^0)^0)=1  ②((1^1)^1)=1。


同样的对于开关2和开关3可以列出相应的式子,此时将开关是否被操作过设置为变量x1,x2,x3。

列出3条方程组,等式右边1表示与初始状态不同,0表示与初始状态相同。

解的数目即为到达终态的情况数目,每组解对应着一个开关操作情况。

对于题目的第一个测试用例列出的异或方程为

①x1^x2^x3=1

②x1^x2^x3=1

③x1^x2^x3=1

可求出自由变量有2个,则情况总数为1<<2=4。

对于求解异或方程组,和普通的加减方程组是一样的,只需要将加减操作换成异或操作,高斯消元化简后求出自由变量的数目。

第二个测试用例列出的矩阵为

1 1 0 1         1 1 0 1

1 1 0 0   →  0 0 0 1            可看到第二行为无解的情况。

0 0 1 1         0 0 1 1

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int A[35][35];
int B[35];
int n;
int ans;

void swap(int a,int b)
{
	int i;
	int temp;
	for (i=0;i<=n-1;i++)
	{
		temp=A[a][i];
		A[a][i]=A[b][i];
		A[b][i]=temp;
	}
	temp=B[a];
	B[a]=B[b];
	B[b]=temp;
}

void gauss()
{
	int i,j,k;
	for (i=0;i<=n-1;i++)
	{
		bool flag=false;
		if (!A[i][i])
		{
			for (j=i+1;j<=n-1;j++)
			{
				if (A[j][i])
				{
					swap(i,j);
					flag=true;
					break;
				}
			}
			if (!flag) continue;
		}

		for (j=i+1;j<=n-1;j++)
		{
			if (A[j][i])
			{
			  for (k=i;k<=n-1;k++)
			    A[j][k]^=A[i][k];
		  	B[j]^=B[i];
			}
		}
	}
	for (i=n-1;i>=0;i--)
	{
		if (A[i][i]==0 && B[i]==1)
		{
			ans=-1;
			break;
		}
		else if (A[i][i]==0)
			ans=ans<<1;
		else
		{
			for (j=i-1;j>=0;j--)
			{
				if (A[j][i])
				{
				  A[j][i]^=A[i][i];
				  B[j]^=B[i];
				}
			}
		}
	}
}


int main()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		ans=1;
		scanf("%d",&n);
		int i;

		int s[35],e[35];

		for (i=0;i<=n-1;i++)
			scanf("%d",&s[i]);
		for (i=0;i<=n-1;i++)
		{
			scanf("%d",&e[i]);
			if (e[i]!=s[i])
				B[i]=1;
			else
				B[i]=0;
		}

		memset(A,0,sizeof(A));
		for (i=0;i<=n-1;i++)
			A[i][i]=1;
		while (1)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			if (!a && !b) break;
			A[b-1][a-1]=1;
		}

		gauss();
		if (ans!=-1)
		  printf("%d\n",ans);
		else
			printf("Oh,it's impossible~!!\n");
	}
	return 0;
}

异或方程组的高斯消元模板:

//有equ个方程,var个变元。增广矩阵行数为equ,列数为var+1,分别为0到var
  int equ,var;
  int a[MAXN][MAXN]; //增广矩阵
  int x[MAXN]; //解集
  int free_x[MAXN];//用来存储自由变元(多解枚举自由变元可以使用)
  int free_num;//自由变元的个数
  
  //返回值为-1表示无解,为0是唯一解,否则返回自由变元个数
  int Gauss()
  {
      int max_r,col,k;
      free_num = 0;
      for(k = 0, col = 0 ; k < equ && col < var ; k++, col++)
      {
          max_r = k;
          for(int i = k+1;i < equ;i++)
          {
              if(abs(a[i][col]) > abs(a[max_r][col]))
                  max_r = i;
          }
          if(a[max_r][col] == 0)
          {
              k--;
              free_x[free_num++] = col;//这个是自由变元
              continue;
          }
          if(max_r != k)
          {
              for(int j = col; j < var+1; j++)
                  swap(a[k][j],a[max_r][j]);
          }
          for(int i = k+1;i < equ;i++)
          {
              if(a[i][col] != 0)
              {
                 for(int j = col;j < var+1;j++)
                      a[i][j] ^= a[k][j];
              }
          }
      }
     for(int i = k;i < equ;i++)//进入此循环的条件:本身矩阵行大于列 或者 因为出现自由变元后使得非自由变元数比行数小
          if(a[i][col] != 0)//若等号右边是1则无解,因为等号左边已经消为0
              return -1;//无解
      if(k < var) return var-k;//自由变元个数
      //唯一解,回代
      for(int i = var-1; i >= 0;i--)
      {
          x[i] = a[i][var];
          for(int j = i+1;j < var;j++)
              x[i] ^= (a[i][j] && x[j]);
      }
      return 0;
  }


你可能感兴趣的:(POJ1830(异或方程组的高斯消元))