BZOJ 1770: [Usaco2009 Nov]lights 燈
1770: [Usaco2009 Nov]lights 燈
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 840 Solved: 397
[Submit][Status][Discuss]
Description
貝希和她的閨密們在她們的牛棚中玩遊戲。但是天不從人願,突然,牛棚的電源跳閘了,所有的燈都被關閉了。貝希是一個很膽小的女生,在伸手不見拇指的無盡的黑暗中,她感到驚恐,痛苦與絕望。她希望您能夠幫幫她,把所有的燈都給重新開起來!她才能繼續快樂地跟她的閨密們繼續玩遊戲! 牛棚中一共有N(1 <= N <= 35)盞燈,編號為1到N。這些燈被置於一個非常複雜的網絡之中。有M(1 <= M <= 595)條很神奇的無向邊,每條邊連接兩盞燈。 每盞燈上面都帶有一個開關。當按下某一盞燈的開關的時候,這盞燈本身,還有所有有邊連向這盞燈的燈的狀態都會被改變。狀態改變指的是:當一盞燈是開著的時候,這盞燈被關掉;當一盞燈是關著的時候,這盞燈被打開。 問最少要按下多少個開關,才能把所有的燈都給重新打開。 數據保證至少有一種按開關的方案,使得所有的燈都被重新打開。
Input
*第一行:兩個空格隔開的整數:N和M。
*第二到第M+1行:每一行有兩個由空格隔開的整數,表示兩盞燈被一條無向邊連接在一起。 沒有一條邊會出現兩次。
Output
第一行:一個單獨的整數,表示要把所有的燈都打開時,最少需要按下的開關的數目。
Sample Input
1 2
1 3
4 2
3 4
2 5
5 3
輸入細節:
一共有五盞燈。燈1、燈4和燈5都連接著燈2和燈3。
Sample Output
輸出細節:
按下在燈1、燈4和燈5上面的開關。
HINT
Source
分析:
我们记$x[i]$为$i$的翻转次数,显然只可能是$0$和$1$,于是我们得到一个$xor$方程:$x[i] xor x[j] xor …… xor x[k]=0/1$,($j……k$代表和$i$直接有边相连的点),$0$就代表最终状态和初始状态相同,$1$代表不同,这样,我们就得到了$n$个异或方程,然后高斯消元解除每个开关点是否需要翻转,消完之后会有自由元也就是无论翻转与否都有一种对应的合法方案的点,对于这种的点我们通过$dfs$来得到最优方案...
我们$dfs$的时候,对于一个主元是否需要翻转我们可以计算得出的,也就是它周围的点对它的影响如果和它本身想要达到的状态不同就需要付出$1$的代价,否则就不用了,由于我们高斯消元的时候把一个$n*n$的矩阵消成了一个对角矩阵,并且$a[i][n+1]$代表第$i$个点是否需要翻转,同时它也代表了所有的是主元的并且和$i$直接有边相连的点对$i$的影响,而$a[i][i]$代表当前的点是否为主元,而且高斯消元完之后的对角矩阵的对角线是由前缀$1$和后缀$0$组成的,所以我们倒着搜索,这样枚举到主元的时候所有的自由元是否翻转已经确定了,所以我们就可以计算所有的和$i$有边相连的点对$i$的影响...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> //by NeighThorn using namespace std; const int maxn=35+5; int n,m,ans,a[maxn][maxn],tmp[maxn]; inline void gauss(void){ for(int i=1;i<=n;i++){ int k=i; while(!a[k][i]&&k<=n) k++; if(a[k][i]){ if(k!=i) for(int s=1;s<=n+1;s++) swap(a[k][s],a[i][s]); for(int l=1;l<=n;l++) if(l!=i&&a[l][i]) for(int s=1;s<=n+1;s++) a[l][s]^=a[i][s]; } } } inline void dfs(int id,int cnt){ if(cnt>=ans) return; if(id==0){ ans=min(ans,cnt); return; } if(a[id][id]){ tmp[id]=a[id][n+1]; for(int i=n;i>id;i--) if(a[id][i]) tmp[id]^=tmp[i]; dfs(id-1,cnt+tmp[id]); } else tmp[id]=0,dfs(id-1,cnt), tmp[id]=1,dfs(id-1,cnt+1); } signed main(void){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) a[i][i]=a[i][n+1]=1; for(int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),a[x][y]=a[y][x]=1; ans=n+1;gauss();dfs(n,0);printf("%d\n",ans); return 0; }
By NeighThorn