• 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=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
    ",ans);
        return 0;
    }
  • 相关阅读:
    使用winmm.dll 获取麦克风声音数据
    什么是拆箱和装箱?
    C#窗体程序【用户控件-窗体】委托事件
    如何在网页标题栏加入logo图标?
    C#汉字转拼音帮助类
    JQuery中$.ajax()方法参数详解
    UEditor独立图片、文件上传模块
    SqlServer2008安装时提示重启计算机失败 解决办法
    如果说人生是自我编写的程序
    LINQ的Any() 方法
  • 原文地址:https://www.cnblogs.com/L-Memory/p/9877089.html
Copyright © 2020-2023  润新知