dfs搜索剪枝技巧

1.当前搜到的东西劣于答案,或者当前搜到的加上之后能取到最最最优的(估价)依然劣于答案,return

2.对于第1点可以在搜前先估一个答案上界

3.按一定顺序枚举,如从小到大或从大到小,减少相同状态

4.对第3点,在一大部分题里从大到小枚更好,因为能更快得到一个答案用于剪掉后面的

5.每层枚举时可判掉一些无用状态

例题:

1.洛谷P1731 [NOI1999]生日蛋糕

链接:https://www.luogu.org/problemnew/show/P1731

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
int n,m,r[20],h[20],ans=2e9;
int sigma(int x)
{return x*(x+1)*(2*x+1)/6;}
int S(int r,int h)
{return 2*r*h;}
void dfs(int pos,int las,int res)
{
    if(res+sigma(m-pos+1)>ans)return;
    if(pos!=1&&las-(m-pos+1)*r[pos-1]*r[pos-1]*h[pos-1]>0)return;
    if(las<=0)return;
    int mnr,mxr,mnh,mxh,tp;
    mxr=min(las,r[pos-1]-1);
    mnr=max(m-pos+1,(int)sqrt(las/(h[pos-1]-1)/(m-pos+1)));
    for(int i=mxr;i>=mnr;i--)
    {
        r[pos]=i;tp=i*i;
        if(pos==m)
        {
            h[pos]=las/tp;
            if(h[pos]<h[pos-1]&&h[pos]*i*i==las)
                ans=min(ans,res+S(i,h[pos])+((pos==1)?tp:0));
        }
        else
        {
            mxh=min(h[pos-1]-1,(las-sigma(m-pos))/tp);
            mnh=m-pos+1;
            for(int j=mxh;j>=mnh;j--)
            {
                h[pos]=j;
                dfs(pos+1,las-tp*j,res+S(i,j)+((pos==1)?tp:0));
            }
        }
    }
}
int main()
{
    cin>>n>>m;
    r[0]=h[0]=1e9;
    dfs(1,n,0);
    ans==2e9?puts("0"):printf("%d\n",ans);
}

2.ZJOI2005 最少乘法次数

#pragma GCC optimize(3,"inline","Ofast")
#define ll unsigned long long
#include<bits/stdc++.h>
using namespace std;
int n,ans=0;
bool vis[2010];
int a[30],mo[20];
void init()
{
    int tp=n,cnt=0;
    while(tp)ans++,tp-=tp&-tp;tp=1;
    if(ans)ans--;
    while(tp<=n)cnt++,tp<<=1;
    ans+=cnt-1;
    ans=min(ans,(int)(4*floor(log2(n))/3+2));
    ans=min(ans,14);
}
void dfs(int pos,int mx)
{
    ll x=0,y=0;
    if(pos>=ans)return;
    if(mx*mo[ans-pos]<n)return;
    for(int i=1;i<=pos;i++)
        for(int j=i;j<=pos;j++)
            if(a[i]+a[j]==n){ans=min(ans,pos);return;}
    for(int i=pos;i>=1;i--)
        for(int j=i;j>=1;j--)
            if(a[i]+a[j]<n&&!vis[a[i]+a[j]])
            {
                vis[a[i]+a[j]]=1;
                a[pos+1]=a[i]+a[j];
                if(a[pos+1]>mx)
                dfs(pos+1,a[pos+1]);
                vis[a[i]+a[j]]=0;
            }
    return;
}
int main()
{
    scanf("%d",&n);
    //freopen("ot.txt","w",stdout);
    init();mo[0]=1;
    for(int i=1;i<=15;i++)mo[i]=mo[i-1]*2;
    a[1]=1,vis[1]=1,dfs(1,1);
    cout<<ans;
}

猜你喜欢

转载自blog.csdn.net/caoyang1123/article/details/82227035
今日推荐