经典剪枝和dfs.木棍

凑木棍啦

看了下面这个恍然大悟(不知是哪位神犇)

#include<cstdio>
int n,q[110],ans=0,tot,maxx,minn;
bool tf=false;
//搜索(深搜) 
void dfs(int d,int x,int t,int p)
//d是还需要拼接的木棍,x是当前拼接的长度,t是每根拼好的木棍要求的长度,p是最大能拼多长的木棍 
{
    if(tf)return;
    //有答案就退出循环 
    if(d==0)
    //全部木棍拼完了,找到答案 
    {
        tot=t;
        //记录答案
        tf=true;
        //标记有答案 
        return;
    }
    if(x==t)
    //拼出了合法木棍 
    {
        dfs(d-1,0,t,maxx);
        //减少还需要拼接的个数,当前已经拼完了一根合法木棍,下一次从头拼接,最大可拼接的木棍还是可以拼所有合法木棍最大的长度
        return;
    }
    for(int i=p;i>=minn;i--)
    // 从最大可以拼接的长度开始(先取大的) 
    {
        if(q[i]>0 && x+i<=t)
        // i长度的木棍还有,且拼接后没有超出目标 
        {
            q[i]--;
            //木棍数量-- 
            dfs(d,x+i,t,i);
            //进入下一层 
            if(tf)return;
            //有答案就退出循环
            q[i]++;
            //木棍数量++ 
            if(x==0)return;
            //优化:如果从dfs出来且没有答案,就是说剩下的木棍无法组成足够的合法木棍,就不用继续往下搜索了
            if(x+i==t)return;
            //优化:(和上一个优化有关)上一个优化跳出了 之后就会直接到这个优化(因为如果还能拼成合法木棍, 即使有x长度的木棍和y长度的木棍,x+y=i,那么也不会有更优,而且也不会拼出足够的目标木棍,就没有继续搜索的必要)
        }
    }
}

int main()
{ 
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int zz;scanf("%d",&zz);
        if(zz<=50)
        //坑人的点 
        {
            q[zz]++;ans+=zz;
            //记录合法木棍的总和 
            maxx=maxx>zz?maxx:zz;
            //记录最大值 
            minn=minn<zz?minn:zz;
            //记录最小值 
        }
    }
    for(int i=maxx;i<=ans/2;i++)
    //从最小可以拼接的长度枚举(木棍中最长的), ans/2是因为枚举的i要是总 和的因子,但超过ans的一半后就不会是ans的因子了
        if(!tf && ans%i==0)
        //当还没有找到答案且i是ans的因子时,就搜索一次 
            dfs(ans/i,0,i,maxx);
    if(!tf)tot=ans;
    //如果没有找到答案,那就是全部拼成一根木棍 
    printf("%d",tot);
    //出答案
    return 0;
}

猜你喜欢

转载自blog.csdn.net/balalalalalalala/article/details/80942224