http://www.lydsy.com/JudgeOnline/problem.php?id=2466
不會做,暴力- - 所以T掉不解釋(n<=100)
正解是高斯消元。
預備知識:矩陣乘法,行列式的基本變換(其實不需要,只是掌握了之後可以把消元的過程看成是行列式轉成上三角的過程),XOR操作.
對於一個不會高斯消元的蒟蒻,看網上的大神的題解都是各種被虐,所以慢慢補一下高斯消元了。
主要理解一下高斯消元是怎麼搞的。(其實就是把未知量的係數搞進係數矩陣,每行都是一個方程,答案可以存在每一行的末尾)
也許很多人都是從一般的解多元一次方程開始看,我是爲了做題,所以只看的求解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;
}