跳转至

动态规划

You:

动态规划做题步骤


ChatGPT:

动态规划(DP)是一种通过分解问题成重叠子问题来求解最优解的方法。下面是动态规划做题的一般步骤:

  1. 明确问题与目标

    • 理解题意:清楚输入、输出及问题要求。

    • 确定特性:确认问题是否具有最优子结构(最优解可以由子问题的最优解构成)和重叠子问题的特点。

  2. 分析子问题

    • 拆分问题:将原问题分解成若干子问题。

    • 子问题之间的关系:找出各个子问题之间的依赖关系,确定它们如何相互影响。

  3. 定义状态

    • 状态表示:明确用 dp 数组(或变量)表示子问题的解。例如,dp[i] 可能表示到第 i 天/步/位置时的最优解。

    • 状态的维度:有时问题需要多维状态,比如二维数组 dp[i][j],用于表示多个变量的状态。

  4. 确定状态转移方程

    • 递推关系:推导出如何利用之前计算的子问题结果得到当前问题的解,即状态之间的转换公式。

    • 数学表达:用数学公式准确描述状态转移的过程,例如

      dp[i] = max(dp[i-1], prices[i] - minprice)
      

      表示当天的最大利润要么等于前一天的利润,要么是当天卖出与之前最低买入价之差的较大值。

  5. 确定初始状态(Base Case)

    • 初始化:为 dp 数组或变量设置初始值,比如 dp[0]minprice = prices[0]

    • 边界条件:处理输入为空或特殊情况,确保递推公式从初始状态开始正确执行。

  6. 确定计算顺序

    • 依赖顺序:根据子问题之间的依赖关系,确定填表(或计算)的顺序。通常是从小规模问题开始逐步扩展到原问题。

    • 迭代还是递归:选择用递归(配合备忘录)还是迭代的方式实现动态规划。

  7. 实现代码并优化

    • 编码:按照前面的步骤将状态定义、状态转移方程和初始状态实现出来。

    • 空间优化:如果发现某些状态只依赖于前一状态,可以用变量替换数组,将空间复杂度从 O(n) 优化到 O(1)。

  8. 测试与验证

    • 边界测试:考虑所有边界情况,确保代码健壮。

    • 样例验证:使用示例数据检验结果是否正确。

按照这些步骤,你可以系统性地思考和解决大部分动态规划问题。


You:

最优子结构和重叠子问题 是什么意思


ChatGPT:

最优子结构重叠子问题是动态规划问题的两个重要性质,它们帮助我们判断一个问题是否适合使用动态规划来解决。


最优子结构

定义
如果一个问题的最优解可以由其子问题的最优解构成,那么这个问题就具有最优子结构性质。

举例说明

  • 最短路径问题:假设你要找从 A 点到 D 点的最短路径,如果路径经过 B 和 C,那么 A 到 D 的最短路径必然是由 A 到 B、B 到 C 和 C 到 D 各段的最短路径构成。

  • 背包问题:在 0-1 背包问题中,若知道在前几件物品中如何获得最大价值,那么在加入第 i 件物品时,可以基于前 i-1 件物品的最优结果构建整体最优解。

总结
最优子结构意味着全局最优解依赖于局部最优解。当我们能够将问题拆解成若干子问题并且知道子问题的最优解时,就可以组合出原问题的最优解。


重叠子问题

定义
如果在分解问题时,很多子问题被重复计算了,那么这些子问题就是重叠子问题。

举例说明

  • 斐波那契数列:计算 F(n) = F(n-1) + F(n-2) 时,会多次计算 F(n-2)F(n-3) 等子问题。如果直接用递归算法,会造成大量重复计算。

  • 路径计数问题:在网格中求从左上角到右下角的路径数,每个位置的路径数可以通过它上方和左边的路径数计算得出,这些中间结果会被多次使用。

总结
重叠子问题意味着子问题在计算过程中会被反复求解。动态规划正是利用记忆化或自底向上填表的方式,避免重复计算,从而提高效率。


结合两者
当一个问题既具有最优子结构(全局最优可以由局部最优构成),又存在重叠子问题(部分子问题重复出现)时,就非常适合使用动态规划来求解。动态规划通过存储子问题的答案,避免了重复计算,同时利用子问题的最优解构建全局最优解。

动态规划和回溯

回到页面顶部