168 生日蛋糕(dfs之剪枝)

1. 问题描述:

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

输入格式

输入包含两行,第一行为整数 N,表示待制作的蛋糕的体积为 Nπ。第二行为整数 M,表示蛋糕的层数为 M。

输出格式

输出仅一行,是一个正整数 S(若无解则 S=0)。

数据范围

1 ≤ N ≤ 10000,
1 ≤ M ≤ 20

输入样例:

100
2

输出样例:
68
来源:https://www.acwing.com/problem/content/170/

2. 思路分析:

分析题目可以知道我们需要在满足体积为Nπ,高度为M,下一层的蛋糕高度与半径都大于上一层蛋糕的高度与半径的前提下找到对应的蛋糕制作方案,也即需要确定每一层蛋糕的高度与半径,使得最终蛋糕的外表面积是最小的,所以我们需要枚举出所有可能的蛋糕制作方案找到最优解使得S最小,这道题目属于dfs剪枝的经典题目。首先我们需要想一个搜索顺序使得可以枚举出所有的答案,我们可以一层一层自底向上搜索,枚举每一层蛋糕可能的高度与直径,这样搜索一定可以搜索出所有的方案,第二个需要考虑的问题对dfs进行剪枝优化:首先我们需要优化搜索顺序,优先搜索分支数目较少的节点,① 我们可以自底向上进行搜索,在枚举蛋糕高度与半径的时候需要使得体积和表面积越大这样后面可以选择的分支数目就比较少,因为半径是平方级别的,所以可以先枚举半径然后再枚举高度,枚举的时候从大到小进行枚举;② 枚举的每一层蛋糕的高度与半径都是有一定范围的,我们需要推导一下r和h的范围,使用R[u]和H[u]来表示第u层蛋糕的半径与高度,R(u) <= R(u + 1),H(u) <= H(u + 1),因为每一层半径与高度是逐层增加的,所以需要满足u <= R(u) <= min{R(u + 1) - 1, √n - v},u <= H(u) <= min{H(u + 1) - 1, (n - v) / (r * r)},其中需要满足体积的限制n - v >= πR * R * H,H最小取1而π是不用管的==> R(u) <= √n-v,也即上面的式子,类似地 n - v >= πR * R * H,令R = 1可以得到高度的限制为H(u) <= (n - v) / (r * r);③ 可以预处理出前u层体积的最小值与表面积的最小值,对于每一层蛋糕来说每一层的高度与半径在上一层的基础加1,最上面的蛋糕高度和半径都为1;预处理之后之后可以进行可行性剪枝与最优性剪枝,v + min(u) <= n,s + min(s) < res;④ 这个剪枝其实比较难想,需要对公式进行放缩:S1~u = ∑2πRkHk(k = 1,2...u)= 2 / Ru+1 ∑πRkHkRu+1 >= 2 / Ru+1∑Rk * Rk * Hk,n - v = ∑πRk * Rk * Hk ==> S1~u >= 2(n - v) / Ru+1,在递归的时候如果发现s + 2(n - v) / Ru+1 > res,也可以退出了;

3. 代码如下:

import math
from typing import List


class Solution:
    res = 0

    def dfs(self, n: int, m: int, u: int, s: int, v: int, R: List[int], H: List[int], minv: List[int], mins: List[int]):
        # 最优性剪枝
        if s + mins[u] >= self.res: return
        # 可行性剪枝
        if v + minv[u] > n: return
        # 下面这个是最难想的剪枝
        if s + 2 * (n - v) // R[u + 1] >= self.res: return
        if u == 0:
            if v == n:
                self.res = s
            return
        # r和h的取值范围
        for r in range(min(R[u + 1] - 1, int(math.sqrt(n - v))), u - 1, -1):
            for h in range(min(H[u + 1] - 1, (n - v) // (r * r)), u - 1, -1):
                t = 0
                # 因为从最上面看的表面积相当于是一个最底层的圆的面积这里特判一下就可以了, 也即加上这一部分的长度
                if u == m:
                    t += r * r
                # 将当前这一层的r和h置为对应的半径和高度
                R[u], H[u] = r, h
                self.dfs(n, m, u - 1, s + 2 * r * h + t, v + r * r * h, R, H, minv, mins)

    # 这道题目是dfs剪枝很经典的题目
    def process(self):
        # n表示总体积, m表示层数
        n = int(input())
        m = int(input())
        # R, H用来记录递归过程中的半径和高度
        R, H = [0] * 30, [0] * 30
        minv, mins = [0] * 30, [0] * 30
        INF = 10 ** 10
        # 预处理出minv和mins列表, 方便后面计算
        for i in range(1, m + 1):
            minv[i] = minv[i - 1] + i * i * i
            mins[i] = mins[i - 1] + 2 * i * i
        # 这两个元素是哨兵
        R[m + 1] = H[m + 1] = INF
        self.res = INF
        self.dfs(n, m, m, 0, 0, R, H, minv, mins)
        if self.res == INF: self.res = 0
        return self.res


if __name__ == "__main__":
    print(Solution().process())
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DFS(深度优先搜索)是一种常见的图遍历算法,它使用递归或栈的方式,从一个顶点出发,沿着一条路径一直到达最深的节点,然后回溯到上一层继续遍历其他节点。DFS常被用于解决图的连通性问题、路径问题等。在实际应用中,可以使用DFS进行状态搜索、图的遍历、拓扑排序等。 剪枝是指在搜索过程中,通过一系列的策略判断,提前终止当前搜索分支,并跳过一些无用的搜索路径,从而减少搜索时间。剪枝的核心在于提前排除某些明显不符合条件的状态,以减少无效搜索的时间开销,提高效率。在算法设计中,剪枝通常会利用一些特定的性质或条件进行判断,从而缩小搜索空间。 动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划通常用于求解最优化问题,它通过定义状态和状态转移方程,采用自底向上的思路,逐步求解每个子问题的最优值,最终得到原问题的最优解。动态规划的核心是存储已经计算过的子问题的解,避免了重复计算。 贪心算法是一种基于局部最优解的策略,它通过每一步选择在当前状态下最优的解,以期望得到全局最优解。贪心算法的基本思想是由局部最优解推导出全局最优解,通常通过贪心选择性质、最优子结构和贪心选择构成三部分。贪心算法相比其他算法,如动态规划,它的优势在于简单、高效,但缺点在于不能保证获取到全局最优解,只能得到一个近似解。 综上所述,DFS剪枝、动态规划和贪心算法算法设计和问题求解中都发挥着重要的作用。具体使用哪种算法取决于问题的性质和要求,需要在实际应用中进行综合考虑和选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值