如何用Top Down法求解钢条切割问题:返回全长度最大收益及切割方案
带记忆化的Top-Down钢条切割解法(含收益列表与切割方案)
嘿,既然你已经搞定了自底向上的钢条切割,那理解Top-Down的思路就很顺了——核心就是递归+备忘录(记忆化),避免重复计算子问题,同时我们可以同步记录每个长度的最优切割点,最后导出完整的切割方案。
整体思路
Top-Down动态规划的逻辑是:
- 从目标长度开始,递归分解成更小的子问题;
- 用备忘录数组存储已经计算过的子问题结果,避免重复计算;
- 同时维护一个切割点数组,记录每个长度对应的最优第一次切割长度,方便后续生成切割方案。
完整代码实现
def cut_rod_top_down(pricelist): n = len(pricelist) - 1 # 目标钢条长度(pricelist[0]对应长度0的收益) # 初始化备忘录:r[i]存储长度i的最大收益,s[i]存储长度i的最优第一次切割长度 r = [-1] * (n + 1) s = [0] * (n + 1) r[0] = 0 # 长度0收益为0 def memoized_cut_rod(length): if r[length] != -1: # 已经计算过,直接返回备忘录的值 return r[length] max_val = 0 best_cut = 0 # 遍历所有可能的切割点:从1到当前length for i in range(1, length + 1): current_val = pricelist[i] + memoized_cut_rod(length - i) if current_val > max_val: max_val = current_val best_cut = i # 记录最优切割点 r[length] = max_val s[length] = best_cut return max_val # 计算从0到n所有长度的最大收益 for length in range(n + 1): memoized_cut_rod(length) # 生成每个长度的切割方案 def get_cut_scheme(length): scheme = [] while length > 0: cut = s[length] scheme.append(cut) length -= cut return scheme # 生成0到n所有长度的切割方案列表 schemes = [get_cut_scheme(i) for i in range(n + 1)] return r, schemes
代码解释
备忘录初始化:
r数组:初始化为-1(表示未计算),r[0]设为0(长度0的钢条收益为0);s数组:存储每个长度的最优第一次切割长度,初始为0。
递归记忆化函数:
- 如果当前长度的收益已经计算过(
r[length] != -1),直接返回; - 遍历所有可能的切割点
i,计算切割成i和length-i两段的总收益,更新最大收益和对应的切割点; - 将计算结果存入备忘录,记录切割点。
- 如果当前长度的收益已经计算过(
生成切割方案:
get_cut_scheme函数通过回溯s数组,从目标长度开始,每次取最优切割点,直到剩余长度为0,得到完整的切割分段方案;- 最后生成从长度0到目标长度的所有方案列表。
测试示例
假设我们的价格列表是pricelist = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30](对应长度0到10的价格),调用函数:
pricelist = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30] max_revenues, cut_schemes = cut_rod_top_down(pricelist) # 打印长度0到10的最大收益 print("各长度最大收益:", max_revenues) # 打印各长度的切割方案 for i in range(len(max_revenues)): print(f"长度{i}的最优切割方案:{cut_schemes[i]}")
输出结果会和自底向上的解法完全一致,但实现方式是自上而下的递归+记忆化。
和自底向上的对比
- Top-Down更符合直观的递归思路,不需要按顺序计算子问题,备忘录会自动处理依赖;
- 两种方法的时间复杂度都是O(n²),空间复杂度都是O(n),效率相近。
内容的提问来源于stack exchange,提问作者JGerulskis
相关产品推荐
相关产品推荐

