深搜的剪枝技巧(二)——生日蛋糕(优化搜索顺序、可行性剪枝,最优性剪枝)

生日蛋糕(优化搜索顺序、可行性剪枝,最优性剪枝)

  • 问题描述

    • Mr. W 要制作一个体积为 \(N\pi\) 的 M 层生日蛋糕,每层都是一个圆柱体,设从下往上数第 i (\(1\leq i \leq M\)) 层蛋糕是半径为 \(R_i\),高度为 \(H_i\) 的圆柱。当 \(i < M\) 时,要求 \(R_i > R_{i+1}\),且 \(H_i > H_{i+1}\)。由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 Q 最小。令 \(Q = S\pi\),请对给出的 N 和 M 编程,找出蛋糕的制作方案(适当的 \(R_i\)\(H_i\) 的值),使 S 最小(除 Q 外,以上所有数据皆为正整数)
  • 输入格式

    • 第一行为 N (\(N \leq 10000\))。表示待制作的蛋糕体积为 \(N\pi\)
    • 第二行为 M (\(M\leq 20\)),表示蛋糕的层数为 M
  • 输出格式

    • 输出仅一行,是一个整数 S (若无解则 S = 0)
  • 样例输入

    100
    2
    
  • 样例输出

    68
    

    附:圆柱公式:体积 \(V = \pi R^2H\);侧面积:\(A^{'} = 2\pi RH\);底面积:\(A = \pi R^2\)

  • 思路分析——枚举每一层可能的高度和半径。确定搜索范围,即底层蛋糕的最大可能半径和最大可能高度。确定搜索顺序,从底层往上搭蛋糕。

  • 剪枝思路

    • 可行性剪枝 1 ——搭建过程中预见到再往上搭,高度已无法安排,或者半径已经无法安排,则停止搭建
    • 可行性剪枝 2 ——搭建过程中发现还没搭的那些层的体积,一定会超过还缺的体积,则停止搭建
    • 可行性剪枝 3 ——搭建过程中发现还没搭的那些层的体积,最大也到不了还缺的体积,则停止搭建
    • 最优性剪枝——搭建过程中发现已建好的面积已经超过目前求得的最优表面积,或者预见到搭建完后面积一定会超过目前最优表面积,则停止搭建
  • 代码(未剪枝,交上去会超时)

    #include <iostream>
    #include <cmath>
    using namespace std;
    
    int N,M;
    int minArea = 1 << 30;
    int area = 0;
    
    void Dfs(int v,int n,int r,int h);
    
    int main()
    {
        cin>>N>>M;
        int MaxR = sqrt(N);   //粗略估计,忽略 π
        int MaxH = N;
        Dfs(N,M,MaxR,MaxH);
        if(minArea == 1 << 30)
            cout<< 0 <<endl;
        else
            cout<< minArea <<endl;
        return 0;
    }
    
    void Dfs(int v,int n,int r,int h)
    {
        if(n == 0)
        {
            if(v)
                return;
            else
            {
                minArea = min(minArea,area);
                return;
            }
        }
        if(v <= 0)
            return;
        for(int rr = r; rr >= n; --rr)
        {
            if(n == M)                           //底面积
                area = rr * rr;
            for(int hh = h; hh >= n; --hh)
            {
                area += 2 * rr * hh;
                Dfs(v-rr*rr*hh,n-1,rr-1,hh-1);
                area -= 2 * rr * hh;
            }
        }
    }
    
  • 代码(剪枝修改过的)

    #include <iostream>
    #include <cmath>
    using namespace std;
    
    int N,M;
    int minArea = 1 << 30;
    int minA[22] = {0},minV[22] = {0};
    int area = 0;
    
    int maxVJudge(int m, int r, int h);
    void Dfs(int v,int n,int r,int h);
    
    int main()
    {
        cin>>N>>M;
        for(int i = 1; i <= M; i++)
        {
            minA[i] = minA[i-1] + 2 * i * i;
            minV[i] = minV[i-1] + i * i * i;
        }
        int MaxH = (N - minV[M-1]) / (M * M) + 1;//最大的高度
        int MaxR = sqrt((N - minV[M-1]) / M) + 1;//最大的体积
        Dfs(N,M,MaxR,MaxH);
        if(minArea == 1 << 30)
            cout<< 0 <<endl;
        else
            cout<< minArea <<endl;
        return 0;
    }
    
    int maxVJudge(int m, int r, int h)
    {
        int maxV = 0;
        for(int i = 0; i < m; i++)
            maxV += (r-i) * (r-i) *(h-i);
        return maxV;
    }
    
    void Dfs(int v,int n,int r,int h)
    {
        if(n == 0)
        {
            if(v)
                return;
            else
            {
                minArea = min(minArea,area);
                return;
            }
        }
        if(v<= 0) // 可行性剪枝1
            return ;
        if(minV[n] > v)//可行性剪枝2
            return ;
        if(maxVJudge(n,r,h) < v)//可行性剪枝3
            return ;
        if(minA[n]+area >= minArea) // 最优性剪枝
            return;
        for(int rr = r; rr >= n; --rr)
        {
            if(n == M)                           //底面积
                area = rr * rr;
            for(int hh = h; hh >= n; --hh)
            {
                area += 2 * rr * hh;
                Dfs(v-rr*rr*hh,n-1,rr-1,hh-1);
                area -= 2 * rr * hh;
            }
        }
    }
    
    
posted @ 2018-08-14 14:38  Nikki_o3o  阅读(571)  评论(0编辑  收藏  举报