异或方程组
以为自己高斯消元学明白了,一考试发现自己啥也不是,补篇博客
首先正常的高斯消元是解方程组,有时候判断有无解,而异或方程组是把加减变成异或,就是不进位加法
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是自由元个数,可以双向搜索优化,砍成根号
由于还没打,就先咕了。。。
予明日所有失败者 赋万千不灭颂歌

浙公网安备 33010602011771号