bzoj2466高斯消元求解XOR方程

http://www.lydsy.com/JudgeOnline/problem.php?id=2466


不会做,暴力- - 所以T掉不解释(n<=100)


正解是高斯消元。

预备知识:矩阵乘法,行列式的基本变换(其实不需要,只是掌握了之后可以把消元的过程看成是行列式转成上三角的过程),XOR操作.


对于一个不会高斯消元的蒟蒻,看网上的大神的题解都是各种被虐,所以慢慢补一下高斯消元了。

我是看的这个:http://wenku.baidu.com/link?url=Z95T7uQuRT41kCI84hAi0vsjjwlVuVH98T-6V9i21agTB0p7ROQhmC6P-NrmvSF2flbj4dzxYiKLgCNZdm7CALSTYDO_yPAGQzm4vKeEA7W


主要理解一下高斯消元是怎么搞的。(其实就是把未知量的系数搞进系数矩阵,每行都是一个方程,答案可以存在每一行的末尾)

也许很多人都是从一般的解多元一次方程开始看,我是为了做题,所以只看的求解XOR方程,其实思想完全一样,只是在实现的时候一个用+,-,*,/一个用^而已。


看这个样例:

3

1 2

1 3


对于XOR方程的系数我们不妨看成是是否关联。

设xi为第i盏灯是否操作(每盏灯至多操作一次,否则就变回来了,浪费次数)。

设bi为第i盏灯是否亮。

设m[i][j]表示第i,j盏灯是否相连。


然后我们得到了这样的方程  (x1*(1,1,1))  ^  (x2*(1,1,0))  ^  (x3*(1,0,1)) =(1,1,1)

解释一下,就是x1*(1,1,1)表示x1是否选择,(1,1,1)表示关联。   如果x1=1,这三盏等都亮;否则,都不亮。

剩下的同理。

整个式子就是x1,x2,x3的操作会对总的灯造成什么影响。 (如果最后都为1表示全亮)

这个式子可以看成一个矩阵乘法       (其实矩阵是关于主对角线对称的,有没有发现)

{1 1 1}                 {x1}                {1}

{1 1 0}        *        {x2}      =       {1}

{1 0 1}                 {x3}                {1}


然后就是利用之前所说的类似于处理上三角的方法消元。

可以看成是         |右边的是方程的解

{1 1 1|1}

{1 1 0|1}

{1 0 1|1}


最后我们可以得到

{1 1 1|1}

{0 1 0|0}

{0 0 1|0}

然后倒着求解。

x3*1=0     x3=0

x2*1=0     x2=0

x1*1   ^  x2*1  ^  x3*1  =1

x1=1

然后就搞完了


有可能处理到有些自由变量,比如处理到某个变量的时候,在后面找不到该个变量为1的了。

自由变量对该行方程的解无影响,但对其他方程可能有影响,最后的时候需要枚举这些求解。


算法:

最后枚举所有的自由变量。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=110;
bool f[maxn][maxn];
int ans[maxn];
int is[maxn];
int n,m;
int tot;
void init()
{
	memset(f,0,sizeof(f));
	memset(is,0,sizeof(is));
	ans[0]=0x3f3f3f3f;
	tot=0;
}

void solve()//高斯消元 
{
	int k=1;
	int j;	
	for(int i=1;;i++)//枚举变量 
	{
		if(i==n+1)
		{
			for(j=k;j<=n;j++)
			{
				if(f[j][n+1]==1)
				{
					puts("-1");
					return ;
				}
			}
			m=k-1;
			return ;
		}
		for(j=k;j<=n;j++)
		{
			if(f[j][i])break;
		}
		if(j==n+1)
		{
			continue;
			is[i]=++tot;
		}
		else
		{
			swap(f[k],f[j]);
		    for(j=k+1;j<=n;j++)
		    {
			    if(f[j][i])for(int l=i;l<=n+1;l++)f[j][l]^=f[k][l];
		    }
		}
		k++;
		if(k>n)break;		
	}
	m=n;
}
void get_ans()
{
	for(int j=n+1,i=m;i;i--)
	{
		for(j--;j&&is[j];j--);
		ans[j]=f[i][n+1];
		for(int k=j+1;k<=n;k++)
		if(f[i][k])ans[j]^=ans[k];
	}
}
int main()
{
	while(scanf("%d",&n)!=EOF&&n)
	{
		init();
		for(int i=1;i<n;i++)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			f[a][b]=f[b][a]=1;
		}
		for(int i=1;i<=n;i++)f[i][i]=f[i][n+1]=1;
		solve();
		for(int i=0;i<1<<tot;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(is[j])ans[j]=(i>>is[j]-1)&1;
			}
			get_ans();
			int cnt=0;
			for(int j=1;j<=n;j++)if(ans[j])cnt++;
			ans[0]=min(ans[0],cnt);
		}
		printf("%d\n",ans[0]);
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值