bzoj1770: [Usaco2009 Nov]lights 燈(折半搜索)

1770: [Usaco2009 Nov]lights 燈

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 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。

Sample Output

3

輸出細節:

按下在燈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=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
} void dfs(int x,ll sta,int tot)//sta是当前状态,ed是末状态
{
if(x==cnt+)
{
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+,sta,tot);
dfs(x+,sta^p[x],tot+);
} int main()
{
bin[]=;for(int i=;i<;i++)bin[i]=bin[i-]<<;
n=read();m=read();
ed=bin[n+]-;
for(int i=;i<=m;i++)
{
int a=read(),b=read();
p[a]|=bin[b];p[b]|=bin[a];//记录每个点能改变那些点的状态
}
for(int i=;i<=n;i++)p[i]+=bin[i];//也能改变当前点的状态
cnt=n/;dfs(,,);
flag=;
cnt=n;dfs(n/+,,);
printf("%d\n",ans);
return ;
}
上一篇:【高斯消元】BZOJ 1770: [Usaco2009 Nov]lights 燈


下一篇:VPN理论简单介绍(转载)