USACO45 lights 电灯(折半搜索)

刚刚上一篇博客才D了队长一发,心里虚的慌......万分感谢队长给讲折半搜索!!听说这题可以高斯消元(可是我不会...貌似折半我也不会)

这题呢,一想到就是爆搜啦,然而,爆搜2^35必跪,折半搜索,就让他变成了2^17+2^18,这下明白一点了吧,然后假设现在要从全灭到全开,状压一下就是一个LL的数了,然后从00000~10101(假设嘛)我们只按左边的按钮,11111~01010只按右边的,那加起来不就是答案啦。

#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
LL Bin[40];
int cnt,mn;
LL p[40],ed;//状压,p[i]代表假如我按i,会对那些造成影响就在相应位上记为1
bool half;
map<LL,int>f;
void dfs(int x,LL now,int d)
{
    if(x==cnt+1)
    {
        if(now==ed)mn=min(mn,d);//到达 
        else
        {
            if(half==false)//第一次,记录到达该状态的最优方案 
            {
                int c=f[now];
                if(c==0||c>d)f[now]=d;
            }
            else //第二次,看看我和其他合并能否到达最终状态 
            {
                int c=f[ed-now];
                if(c!=0)mn=min(mn,c+d);
            }
        }
        return ;
    }
    dfs(x+1,now,d);//穷举状态 
    dfs(x+1,now^p[x],d+1);
}  
int main()  
{
    int n,m,x,y;
    scanf("%d%d",&n,&m);
    p[1]=Bin[1]=1;for(int i=2;i<=n+1;i++)p[i]=Bin[i]=Bin[i-1]*2;  
    ed=Bin[n+1]-1;//结束状态就是全为1 
    
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        p[x]+=Bin[y];p[y]+=Bin[x];//记录所影响的 
    }
    
    mn=2147483647;
    half=false;cnt=n/2;dfs(1,0,0);//折半搜索 
    half=true;cnt=n;dfs(n/2+1,0,0);
    printf("%d
",mn);
    return 0;  
}  
原文地址:https://www.cnblogs.com/AKCqhzdy/p/7725535.html