异或方程组

以为自己高斯消元学明白了,一考试发现自己啥也不是,补篇博客
首先正常的高斯消元是解方程组,有时候判断有无解,而异或方程组是把加减变成异或,就是不进位加法
1.判断解的个数
例题:开关问题
这个大体方法就和解方程一样,把加减变成异或,方法一样,最后数出来自由元个数i,2的i次方即是答案

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
bool a[35],b[35];
bool c[40][40];
int mi(int x)
{
	int ans=1;
	while(x--)
    ans*=2;
	return ans;	
}
signed main()
{ 
	ios::sync_with_stdio(0);
	int k;
	cin>>k;
	while(k--)
	{
      memset(a,0,sizeof(a));
      memset(b,0,sizeof(b));
      memset(c,0,sizeof(c));
	  int n;cin>>n;	
	  for(int i=1;i<=n;i++)cin>>a[i];
	  for(int i=1;i<=n;i++)cin>>b[i];
	  for(int i=1;i<=n;i++)a[i]^=b[i];
	  int x,y;
	  while(cin>>x>>y&&(x|y))
	    c[y][x]=1;
	  for(int i=1;i<=n;i++)c[i][i]=1;
	  for(int i=1;i<=n;i++)
	  {
	    int p=i;
	    while(!c[i][p])
	    {
	  	  if(p>n)
		  {
	  		i=n+1;break;
		  }
          int j=i+1;
	  	  while(c[j][p]==0&&j<=n)j++;
		  if(j>n){p++;continue;}
		  for(int k=p;k<=n;k++)swap(c[i][k],c[j][k]);
	      swap(a[i],a[j]);
	    }
	    for(int j=i+1;j<=n;j++)
	    {
	  	  if(!c[j][p])continue;
		  for(int k=p;k<=n;k++)
	  	  c[j][k]^=c[i][k];
	  	  a[j]^=a[i];
	    }
	  } 
	  bool stop=0;
	  int zy=0;
	  for(int i=n;i>=1;i--)
	  {  
	    int con=0;
		for(int j=n;j>=1;j--)
	     if(c[i][j])
	       {  
	  	    con=1;break;
	       }
	    if(con)continue;
	    if(a[i])
	    {
	  	 cout<<"Oh,it's impossible~!!"<<endl;stop=1;break;
		  }
	    zy++;	
	  }
	  if(!stop)cout<<mi(zy)<<endl;
    }
	return 0;
}   

2.具体求解
这个就是那个树内道题,对于每个自由元爆搜取值回代,上面只是判断,这里多了一个求解的过程
每个方程解是0或1代表按不按,最后对2的j次方种状态取所有方程解的和最小值即是答案

#include <bits/stdc++.h>
using namespace std;
bool a[105][105],b[105],c[105],d[105];
int n;int ans=0x3f3f3f;
void guass()
{
	for(int i=1;i<=n;i++)
	{
		int p=i;bool go=0;
		while(!a[p][i])
		{
			if(p>n){go=1;break;}
			p++;
		}
		if(go)continue;
		for(int k=i;k<=n;k++)swap(a[i][k],a[p][k]);
		swap(b[i],b[p]);	
		for(int j=i+1;j<=n;j++)
		{
			if(!a[j][i])continue;
			for(int k=i;k<=n;k++)a[j][k]^=a[i][k];
			b[j]^=b[i];
		}
	}
	memcpy(d,b,sizeof(b)); 
}
void dfs(int x)
{
	 if(!x)
	 {
	 	int an=0;
	 	for(int i=1;i<=n;i++)an+=(int)c[i];
	 	ans=min(ans,an);
	 	return ;
	 }
	 if(a[x][x])
	 {
	 	for(int i=n;i>x;i--)
	 	{
	 		if(!a[x][i])continue;
	 		b[x]^=a[x][i]&c[i];
		 }
		if(a[x][x]==b[x])c[x]=1;
		else c[x]=0;
		dfs(x-1);
	 } 
	 else
	 {
	 	c[x]=0;dfs(x-1);
	 	for(int i=1;i<=x;i++)
	 	 b[i]=d[i],c[i]=0;
	 	c[x]=1;dfs(x-1);
	 }
}
int main()
{
	scanf("%d",&n);
	while(n)
	{
		ans=0x3f3f3f;
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		memset(c,0,sizeof(c));
		memset(d,0,sizeof(d));
		for(int i=1;i<=n-1;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			a[x][y]=a[y][x]=1;
		}
		for(int i=1;i<=n;i++)a[i][i]=1;
		for(int i=1;i<=n;i++)b[i]=1;
		guass();dfs(n);
	    printf("%d\n",ans);
        scanf("%d",&n);
	}
	return 0;
}  

注意这句话 b[x]^=a[x][i]&c[i],最关键的一句,我位与打成异或就无了……
3.优化
爆搜复杂度是2的i次方,i是自由元个数,可以双向搜索优化,砍成根号
由于还没打,就先咕了。。。

posted @ 2021-05-29 14:57  D'A'T  阅读(430)  评论(1)    收藏  举报