bzoj1770: [Usaco2009 Nov]lights 燈(折半搜索)
1770: [Usaco2009 Nov]lights 燈
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1153 Solved: 564
[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。
1 2
1 3
4 2
3 4
2 5
5 3
輸入細節:
一共有五盞燈。燈1、燈4和燈5都連接著燈2和燈3。
Sample Output
3
輸出細節:
按下在燈1、燈4和燈5上面的開關。
輸出細節:
按下在燈1、燈4和燈5上面的開關。
HINT
Source
/* 折半搜索经典题 注意每个点随时都可以去。 搜一半,map记录这一半所有状态的最小步数。 搜另一半时,用当前状态步数+记录好的当前补集步数即可。 */ #include<bits/stdc++.h> #define inf 1000000000 #define N 40 #define ll long long using namespace std; int n,m,cnt,ans=inf; int a[N]; bool flag; ll ed,p[N],bin[N]; map<ll,int>step; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void dfs(int x,ll sta,int tot)//sta是当前状态,ed是末状态 { if(x==cnt+1) { if(sta==ed)ans=min(tot,ans); if(!flag) { int t=step[sta]; if(!t || t>tot)step[sta]=tot; } else { int t=step[ed-sta];//取记录好的当前状态的补集 if(!t)return; ans=min(t+tot,ans); } return; } dfs(x+1,sta,tot); dfs(x+1,sta^p[x],tot+1); } int main() { bin[1]=1;for(int i=2;i<40;i++)bin[i]=bin[i-1]<<1; n=read();m=read(); ed=bin[n+1]-1; for(int i=1;i<=m;i++) { int a=read(),b=read(); p[a]|=bin[b];p[b]|=bin[a];//记录每个点能改变那些点的状态 } for(int i=1;i<=n;i++)p[i]+=bin[i];//也能改变当前点的状态 cnt=n/2;dfs(1,0,0); flag=1; cnt=n;dfs(n/2+1,0,0); printf("%d\n",ans); return 0; }
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。