您的位置:首页 > 其它

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

2013-12-16 01:12 435 查看
对于式子(((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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: