跳转到内容

最优子结构(动态规划)

来自代码酷


最优子结构动态规划问题的核心性质之一,指一个问题的最优解包含其子问题的最优解。这一性质使得我们可以通过组合子问题的解来构造原问题的解,从而避免重复计算。

定义与原理[编辑 | 编辑源代码]

若问题满足以下条件,则称其具有最优子结构

  • 问题可以分解为若干相互重叠的子问题
  • 子问题的最优解能组合成原问题的最优解
  • 子问题在分解过程中会重复出现

数学表达为:若原问题P的最优解由子问题P1,P2,...,Pk的最优解构成,则存在关系: OPT(P)=f(OPT(P1),OPT(P2),...,OPT(Pk)) 其中OPT表示最优解,f为组合函数。

示例分析[编辑 | 编辑源代码]

经典案例:斐波那契数列[编辑 | 编辑源代码]

斐波那契数列定义:F(n)=F(n1)+F(n2),其中F(0)=0,F(1)=1

def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

输入/输出示例

  • 输入:5
  • 输出:5(因为F(5)=5)
  • 子问题树:

graph TD F5 --> F4 & F3 F4 --> F3 & F2 F3 --> F2 & F1 F2 --> F1 & F0

实际应用:钢条切割问题[编辑 | 编辑源代码]

给定长度为n的钢条和价格表pi,求最大收益rn

递推关系: rn=max1in(pi+rni)

def cut_rod(p, n):
    if n == 0:
        return 0
    q = -float('inf')
    for i in range(1, n+1):
        q = max(q, p[i] + cut_rod(p, n-i))
    return q

输入/输出示例

  • 输入:p = [0,1,5,8,9,10,17,17,20,24,30], n=4
  • 输出:10(切割为2+2时收益最大)

识别最优子结构[编辑 | 编辑源代码]

判断问题是否具有最优子结构的三步骤:

  1. 验证问题可分解为子问题
  2. 证明子问题的最优解能构成全局最优解
  3. 确认子问题相互独立(无后效性)

与非最优子结构问题的对比[编辑 | 编辑源代码]

特征 具有最优子结构 非最优子结构
子问题关系 可组合成全局解 子解无法组合
示例 最短路径问题 最长路径问题
动态规划适用性 适用 不适用

常见误区[编辑 | 编辑源代码]

  • 错误假设:认为所有可分解问题都具有最优子结构
  • 反例:无权图的最长简单路径问题
  • 关键区别:子问题是否相互独立

扩展应用[编辑 | 编辑源代码]

矩阵链乘法[编辑 | 编辑源代码]

寻找矩阵相乘的最优顺序,使得标量乘法次数最少: m[i,j]=minik<j(m[i,k]+m[k+1,j]+pi1pkpj)

最优二叉搜索树[编辑 | 编辑源代码]

给定关键字概率分布,构造平均查找时间最短的二叉搜索树: e[i,j]=minirj(e[i,r1]+e[r+1,j]+w(i,j))

总结[编辑 | 编辑源代码]

最优子结构是动态规划可行性的基础,其核心特征是:

  • 问题可分解为重叠子问题
  • 局部最优解可构造全局最优解
  • 子问题独立无后效

掌握这一概念需要:

  1. 理解递归分解过程
  2. 练习经典问题建模
  3. 识别问题是否适用动态规划

模板:编程概念导航