JZOJ1321. 灯

来源:互联网 发布:海尔软件收入 编辑:程序博客网 时间:2024/04/29 21:26

Description

 贝希和她的闺密们在她们的牛棚中玩游戏。但是天不从人愿,突然,牛棚的电源跳闸了,所有的灯都被关闭了。贝希是一个很胆小的女生,在伸手不见拇指的无尽的黑暗中,她感到惊恐,痛苦与绝望。她希望您能够帮帮她,把所有的灯都给重新开起来!她才能继续快乐地跟她的闺密们继续玩游戏!
  牛棚中一共有N(1 <= N <= 35)盏灯,编号为1到N。这些灯被置于一个非常复杂的网络之中。有M(1 <= M <= 595)条很神奇的无向边,每条边连接两盏灯。
  每盏灯上面都带有一个开关。当按下某一盏灯的开关的时候,这盏灯本身,还有所有有边连向这盏灯的灯的状态都会被改变。状态改变指的是:当一盏灯是开着的时候,这盏灯被关掉;当一盏灯是关着的时候,这盏灯被打开。
  问最少要按下多少个开关,才能把所有的灯都给重新打开。
  数据保证至少有一种按开关的方案,使得所有的灯都被重新打开。

分析

看到这里的n很小,只有35,就会想到暴力,
可是如果枚举每一个开关的状态,就是235
这是一个不可以接受的复杂度。
我们就可以考虑折半搜索。
就拿35盏灯来举例
我们将开关分成第1~17和第18~35两个部分。
先用217来求出第1~17开关的所有开关状态的对应全局每盏灯的亮的情况。
也就是说我们知道某个全局灯亮的情况对应着需要使用多少个1~17的开关。
这些提前求出了的东西就用一个哈希表存着。

接下来就考虑第18~35个开关,
同样我们枚举每个开关的开与关,也可以知道此时全局灯的亮的情况。
将这些情况与之前求出的状态在哈希表中对应,即可求出答案。

code

#include <cstdio>#include <algorithm>#include <cstring>#include <string.h>#include <cmath>#include <math.h>#define N 40#define mo 123454321#define ll long longusing namespace std;int n,m,x,y,d;int tot,next[N*N],a[N*N],b[N],f[N],g[mo],ans;ll sum,z[N],h[mo];char ch;void read(int &n){    n=0;    ch=getchar();    while((ch<'0' || ch>'9') && ch!='-')ch=getchar();    int w=1;    if(ch=='-')w=-1,ch=getchar();    while('0'<=ch && ch<='9')n=n*10+ch-'0',ch=getchar();    n*=w;}void ins(int x,int y){    next[++tot]=b[x];    a[tot]=y;    b[x]=tot;}void add(int x){    f[x]=1-f[x];    for(int i=b[x];i;i=next[i])        f[a[i]]=1-f[a[i]];}void hs(ll s,int y){    int x=s%mo;    while(h[x]!=0 && h[x]!=s)x=(x+1)%mo;    g[x]=min(g[x],y);    h[x]=s;}int find(ll s){    int x=s%mo;    while(h[x]!=0 && h[x]!=s)x=(x+1)%mo;    if(h[x]==s)return g[x];else return mo;}void pre(int x,int s){    if(x>n)    {        sum=0;        for(int i=1;i<=n;i++)            sum=sum+f[i]*z[i-1];        hs(sum,s);        return;    }    add(x);    pre(x+1,s+1);    add(x);    pre(x+1,s);}void dg(int x,int s){    if(s>=ans)return;    if(x>d)    {        sum=0;        for(int i=1;i<=n;i++)            sum=sum+z[i-1]-f[i]*z[i-1];        ans=min(ans,find(sum)+s);        return;    }    add(x);    dg(x+1,s+1);    add(x);    dg(x+1,s);}int main(){    z[0]=1;    for(int i=1;i<N;i++)        z[i]=z[i-1]*2;    read(n);read(m);    for(int i=1;i<=m;i++)        read(x),read(y),ins(x,y),ins(y,x);    d=n/2;    memset(g,127,sizeof(g));    pre(d+1,0);    memset(f,0,sizeof(f));    ans=2147483647;    dg(1,0);    printf("%d",ans);}
原创粉丝点击