钢条切割

概述

钢条切割问题是动态规划的入门经典问题:给定一根长度为 的钢条和价格表 ,求切割方案使总收益最大。该问题同时展示了最优子结构重叠子问题两个核心性质,是理解动态规划思想的最佳起点。

定义

钢条切割问题(Rod Cutting)

输入:长度为 的钢条,价格表 ,其中 表示长度为 的钢条的售价。

输出:一个切割方案,使得所有切割段的售价之和最大。

为长度为 的钢条的最大收益,则:

其中 (长度为0的钢条收益为0)。

核心性质

1. 最优子结构

长度为 的钢条的最优切割方案中,第一刀切下长度 后,剩余长度 的钢条也必须按最优方式切割。否则,将剩余部分替换为更优的切割方案,可得到更大的总收益——矛盾。

这意味着我们可以将问题分解为:选择第一刀的位置 ,然后对剩余部分递归求解。

2. 重叠子问题

朴素递归求解时,子问题被大量重复计算。例如,计算 时:

  • 路径
  • 路径 (通过
  • 路径
  • 路径 (通过

被计算了4次, 被计算了2次。朴素递归的时间复杂度为

3. 自顶向下(备忘录)实现

MEMOIZED-CUT-ROD(p, n):
    let r[0..n] be a new array, initialized to -∞
    return MEMOIZED-CUT-ROD-AUX(p, n, r)

MEMOIZED-CUT-ROD-AUX(p, n, r):
    if r[n] >= 0:
        return r[n]
    if n == 0:
        q = 0
    else:
        q = -∞
        for i = 1 to n:
            q = max(q, p[i] + MEMOIZED-CUT-ROD-AUX(p, n-i, r))
    r[n] = q
    return q

4. 自底向上实现

BOTTOM-UP-CUT-ROD(p, n):
    r[0] = 0
    for j = 1 to n:
        q = -∞
        for i = 1 to j:
            q = max(q, p[i] + r[j-i])
        r[j] = q
    return r[n]

5. 构造最优解

为了不仅得到最大收益,还要得到具体的切割方案,需要维护一个辅助数组 ,记录第一刀的最优切割位置:

EXTENDED-BOTTOM-UP-CUT-ROD(p, n):
    r[0] = 0
    for j = 1 to n:
        q = -∞
        for i = 1 to j:
            if q < p[i] + r[j-i]:
                q = p[i] + r[j-i]
                s[j] = i    // 记录第一刀切下长度 i
        r[j] = q
    return r and s

PRINT-CUT-ROD-SOLUTION(p, n):
    while n > 0:
        print s[n]
        n = n - s[n]

复杂度分析

方法时间复杂度空间复杂度
朴素递归(递归栈)
备忘录(自顶向下)
自底向上

章节扩展

参见