JZOJ1321. 灯

13 篇文章 0 订阅

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 long
using 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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值