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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章