bzoj1770-------(算法模板系列之gauss消元异或方程组)

来源:互联网 发布:c语言调用java接口 编辑:程序博客网 时间:2024/06/17 10:11

异或方程组就是形如这个样子的方程组:

M[0][0]x[0]^M[0][1]x[1]^…^M[0][N-1]x[N-1]=B[0]
M[1][0]x[0]^M[1][1]x[1]^…^M[1][N-1]x[N-1]=B[1]

M[N-1][0]x[0]^M[N-1][1]x[1]^…^M[N-1][N-1]x[N-1]=B[N-1]

其中“^”表示异或(XOR, exclusive or),M[i][j]表示第i个式子中x[j]的系数,是1或者0。B[i]是第i个方程右端的常数,是1或者0。

解这种方程可以套用高斯消元法,只须将原来的加减操作替换成异或操作就可以了,两个方程的左边异或之后,它们的公共项就没有了。

具体的操作方法是这样的:对于k=0..N-1,找到一个M[i][k]不为0的行i,把它与第k行交换,用第k行去异或下面所有M[i][j]不为0的行i,消去它们的第k个系数,这样就将原矩阵化成了上三角矩阵;最后一行只有一个未知数,这个未知数就已经求出来了,用它跟上面所有含有这个未知数的方程异或,就小觑了所有的着个未知数,此时倒数第二行也只有一个未知数,它就被求出来了,用这样的方法可以自下而上求出所有未知数。

(以上摘自:边城水手的专栏)



1770: [Usaco2009 Nov]lights 燈

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 799  Solved: 377
[Submit][Status][Discuss]

Description

貝希和她的閨密們在她們的牛棚中玩遊戲。但是天不從人願,突然,牛棚的電源跳閘了,所有的燈都被關閉了。貝希是一個很膽小的女生,在伸手不見拇指的無盡的黑暗中,她感到驚恐,痛苦與絕望。她希望您能夠幫幫她,把所有的燈都給重新開起來!她才能繼續快樂地跟她的閨密們繼續玩遊戲! 牛棚中一共有N(1 <= N <= 35)盞燈,編號為1到N。這些燈被置於一個非常複雜的網絡之中。有M(1 <= M <= 595)條很神奇的無向邊,每條邊連接兩盞燈。 每盞燈上面都帶有一個開關。當按下某一盞燈的開關的時候,這盞燈本身,還有所有有邊連向這盞燈的燈的狀態都會被改變。狀態改變指的是:當一盞燈是開著的時候,這盞燈被關掉;當一盞燈是關著的時候,這盞燈被打開。 問最少要按下多少個開關,才能把所有的燈都給重新打開。 數據保證至少有一種按開關的方案,使得所有的燈都被重新打開。

Input

*第一行:兩個空格隔開的整數:N和M。

*第二到第M+1行:每一行有兩個由空格隔開的整數,表示兩盞燈被一條無向邊連接在一起。 沒有一條邊會出現兩次。

Output

第一行:一個單獨的整數,表示要把所有的燈都打開時,最少需要按下的開關的數目。

Sample Input

5 6
1 2
1 3
4 2
3 4
2 5
5 3

輸入細節:

一共有五盞燈。燈1、燈4和燈5都連接著燈2和燈3。

Sample Output

3

輸出細節:

按下在燈1、燈4和燈5上面的開關。

HINT

Source

Gold





首先,可以看出按多次开关显然没有必要,所以只有按和不按两个选项。
我们列异或方程组


a[i][j]*x[j]^a[i][j2]*x[j2].....=b[i]
.....


其中x[j]表示按或不按j这个灯的开关,a[i][j]表示i和j是否有边连通,如果有则a[i][j]=1否则a[i][j]=0,b[i]表示i在所有操作结束后的状态(关:0,开:1)本题中b[i]均为1

这样列方程由其意义很容易知道,答案=x[i]的和

gauss消元解异或方程组。但是消成三角矩阵后,会发现有一些a[i][i]=0,这就意味着,无论x[i]为1或0,对最终状态无影响,那我们dfs它为0或1,搜出最优解即可。


模板题,今天应该差不多学会gauss了
代码:

#include<iostream>#include<algorithm>#include<cstdio>#include<cmath>using namespace std;int a[55][55],n,m,as=1<<26,tot,ans[55];;void gauss(){for(int i=1;i<=n;i++){int j=i;while(j<=n&&!a[j][i])j++;if(j>n)continue;if(i!=j)for(int k=1;k<=n+1;k++)swap(a[i][k],a[j][k]);for(int j=1;j<=n;j++)if(i!=j&&a[j][i])for(int k=1;k<=n+1;k++)a[j][k]^=a[i][k];}}void dfs(int now){if(tot>=as)return;if(!now){as=min(as,tot);return;}if(a[now][now]){int t=a[now][n+1];for(int i=now+1;i<=n;i++)if(a[now][i])t^=ans[i];ans[now]=t;if(t)tot++;dfs(now-1);if(t)tot--;}else{tot++;ans[now]=1;dfs(now-1);tot--;ans[now]=0;dfs(now-1);}}int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)a[i][i]=1,a[i][n+1]=1;for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);a[x][y]=a[y][x]=1;}gauss();/*for(int i=1;i<=n;i++){for(int j=1;j<=n+1;j++)printf("%d ",a[i][j]);printf("\n");}*/dfs(n);printf("%d\n",as);//while(1);}




边城水手的专栏

边城水手的专栏

边城水手的专栏

1 0
原创粉丝点击