• 关于dfs剪枝技巧研究简记


    例一、数的划分

    个人觉得最剪枝的方法是直接枚举答案 

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    int n,m,ans;
    inline void dfs(int sum,int k,int last)
    {
        if(k==1)//可行性剪枝 
        {
            ++ans;
            re ;
        }
        
        inc(i,last,(n-sum)/k)//上下界剪枝 
            dfs(sum+i,k-1,i);
    }
    int main()
    {
        rd(n);rd(m);
        dfs(0,m,1);
        printf("%d",ans);
        re 0;
    } 
    View Code

    例二、[NOI1999]生日蛋糕

    疯狂剪枝,你就赢了

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    #define dec(i,l,r) for(int i=r;i>=l;--i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    int n,m,ans=2147483647,minv[25],mins[25];
    inline void pre()
    {
        inc(i,1,m)
        {
            minv[i]+=minv[i-1]+i*i*i;
            mins[i]+=mins[i-1]+2*i*i;
        }
        
    }
    
    inline void dfs(int nowv,int nows,int dep,int lastr,int lasth)
    {
        if(!dep)
        {
            if(nowv==n)
            ans=min(ans,nows);
            re ;
        } 
        if(nows+mins[dep]>=ans)re;//最优性剪枝一:当前表面积+上面层取最小表面积大于当前答案 
        if(nowv+minv[dep]>n)re ;//可行性剪枝:是否满足体积为N 
        if(2*(n-nowv)/lastr+nows>=ans)re ;//不等式放缩得到的最优性剪枝二 
        int nowmaxv=n-minv[dep-1]-nowv; //同下 
        dec(i,dep,min((int)sqrt(nowmaxv/dep),lastr-1))//枚举r
        //上下界剪枝
        //+搜索顺序优化 
        {
            dec(j,dep,min(nowmaxv/i/i,lasth-1))//枚举h 
            dfs(i*i*j+nowv,2*i*j+nows,dep-1,i,j);
        } 
    } 
    int main()
    {
        freopen("in.txt","r",stdin);
        rd(n),rd(m);
        pre();
        
        int nowmaxv=n-minv[m-1]; //当前层最大体积 
        dec(i,m,(int)sqrt(nowmaxv/m))//枚举r
        {
            dec(j,m,nowmaxv/i/i)
            dfs(i*i*j,i*i+2*i*j,m-1,i,j);//最下层,表面积要加上底面积 
        } 
        printf("%d",ans);
        re 0;
    } 
    View Code

    例三、小木棍 

    题目描述

    乔碧萝殿下有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050。

    现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

    给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(int i=l;i<=r;++i) 
    #define dec(i,l,r) for(int i=l;i>=r;--i)
    
    using namespace std;
    int n,maxx,a[66],use[66],k,x,sum,m,len,next[66]; 
    inline int read()
    {
        int date=0;char c=' ';
        while('0'>c||c>'9')c=getchar();
        while('0'<=c&&c<='9'){date=(date<<3)+(date<<1)+(c^48);c=getchar();}
        re date;
    }
    
    void vv(int num,int last,int rest)
    {
        if(rest==0)//如果拼接好当前木棍 
        {
            if(num==m-1)printf("%d",len),exit(0);//找到答案 
            inc(i,2,n)
            if(!use[i])
            {    use[i]=1;
                vv(num+1,i,sum/m-a[i]);//进行下一轮 
                use[i]=0;break;    }            
        }
        
        int l=last+1, r=len, mid;
        while(l<r)
        {
            mid=(l+r)>>1;
            if(a[mid]<=rest) r=mid;
            else l=mid+1;
        }
        inc(i,l,k)//上下界剪枝&&顺序 
        if(!use[i]&&a[i]<=rest)
        {    
            use[i]=1;
            vv(num,i,rest-a[i]);
            use[i]=0;
            if(rest==a[i] || rest==len) return; //当前木棍不可行 ,无论如何都不可行 
            i=next[a[i]];//跳过相同长度 
        }
        re;
    }
    
    bool cmp(int a,int b)
    {
        re a>b;
    }
    
    int main()
    {
    //    freopen("testdata.in","r",stdin);
        n=read();
        inc(i,1,n)
        {
            x=read();
            if(x<=50){a[++k]=x;sum+=x;
                maxx=max(maxx,x);}
        }
        
        sort(a+1,a+k+1,cmp);//从大到小枚举 
        //优化搜索顺序 
        
        inc(i,1,k)
        {
            int j=i+1;
            if(a[i]==a[j])++j;
            next[a[i]]=j-1;    //优化搜索顺序 
        }
        
        int i;use[1]=1;
        for(i=maxx;i<sum;++i)//上下界剪枝 
        {
        
            if(sum%i)continue;
            m=sum/i;
            len=i;
            memset(use,0,sizeof(use));
            vv(0,1,len-a[1]);
        }
        printf("%d",i);
        re 0;
    }
    
     
    View Code

    例 4 Addition Chains

    好好的一到迭代加深

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(int i=l;i<=r;++i) 
    #define dec(i,l,r) for(int i=l;i>=r;--i)
    
    using namespace std;
    long long n,m,a[1005],deep;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    inline bool dfs(int cnt)
    {
        if(cnt==deep)
            re a[cnt]==n;
        if(1ll*a[cnt]*(1<<(deep-cnt))<n)re 0;
        //可行性剪枝,当前最大数2倍跳到最后能否大于n? 
        inc(i,1,cnt)
        inc(j,i,cnt)
        if(a[i]+a[j]>n)break;
        else if(a[i]+a[j]>a[cnt])
        {
            a[cnt+1]=a[i]+a[j];
            if(dfs(cnt+1)) re 1;
        }
        re 0;
    }
    
    int main()
    {
    //    freopen("testdata.in","r",stdin);
        a[1]=1;
        while(2333)
        { 
            scanf("%d",&n);
            if(!n)re 0;
            printf("1");
            for(deep=1;;++deep)
            if(dfs(1))
            {
                inc(j,2,deep)
                printf(" %d",a[j]);
                break;
            }    
            printf("
    ");
        } 
        re 0;
    }
    
     
    View Code

    非要弄成普通深搜剪枝

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(int i=l;i<=r;++i) 
    #define dec(i,l,r) for(int i=l;i>=r;--i)
    
    using namespace std;
    int n,m,a[105],b[105],ans;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    inline void dfs(int cnt)
    {
        if(cnt>=ans) re;
        if(a[cnt]==n)
        {
            inc(i,1,cnt)
            b[i]=a[i];
            ans=cnt;
            re ;
        }
        for(int i=cnt;(a[i]<<1)>a[cnt];--i)
        {
            dec(j,i,1)
            if(a[j]+a[i]<=a[cnt])break;
            else if(a[i]+a[j]<=n)
            {
                a[cnt+1]=a[i]+a[j];
                dfs(cnt+1);
            }
        }
    }
    
    int main()
    {
    //    freopen("testdata.in","r",stdin);
        a[1]=1;a[2]=2;
        while(scanf("%d",&n))
        { 
        if(!n)re 0;
            if(n==1)
            printf("1");
            else if(n==2)
            printf("1 2");
            else 
            {
                ans=2147483647;
                dfs(2);
                inc(i,1,ans)
                printf("%d ",b[i]);
            }
            
            printf("
    ");
        } 
        re 0;
    }
    
     
    View Code
  • 相关阅读:
    React学习笔记(六)事件处理
    React学习笔记(五)State&声明周期
    学会装逼,你的人生可能会开挂
    Go指南
    JavaScript检测数据类型
    $.on()方法和addEventListener改变this指向
    JavaScript返回上一页
    js继承
    js原型二
    全局变量与局部变量
  • 原文地址:https://www.cnblogs.com/lsyyy/p/11443885.html
Copyright © 2020-2023  润新知