返回

踏入动态规划的殿堂:从本质到实战应用

闲谈

什么是动态规划?

动态规划(Dynamic Programming)是解决一系列问题的有效算法,其精髓在于化繁为简,将复杂问题拆解为更小的重叠子问题。它起源于递归算法,但通过引入备忘录,避免了重复计算,大幅提升了效率。

动态规划的优势

  • 效率提升: 动态规划避免了对同一子问题的重复计算,大大提高了算法效率。
  • 清晰简洁: 相比递归算法,动态规划的代码往往更清晰易懂,逻辑更为直观。
  • 通用性强: 动态规划适用于解决具有重叠子问题的各种问题,包括最长公共子序列、背包问题和最短路径问题。

自顶向下与自底向上

动态规划有两种主要的求解策略:自顶向下和自底向上。

  • 自顶向下(递归): 从问题的高层开始,逐层细化,直到达到基本子问题。这种方法直观,但计算量较大。
  • 自底向上(迭代): 从基本子问题出发,逐层递推,最终解决原始问题。这种方法更有效率,但代码编写难度稍高。

备忘录的妙用

备忘录是一种数据结构,用来存储子问题的解。通过查询备忘录,动态规划算法可以避免重复计算,大大提升效率。

实战应用

动态规划在实际应用中发挥着重要作用。以下是一些经典的动态规划问题:

  • 最长公共子序列:找出两个序列中最长的公共子序列。
  • 背包问题:在限定的容量下,从一组物品中挑选出总价值最大的组合。
  • 最短路径问题:找出图中两个点之间的最短路径。

案例分析:最长公共子序列

假设我们有两个序列 A 和 B,分别为“ABCD”和“CBACD”。我们要找出这两个序列的最长公共子序列。

自顶向下解法:

  1. 定义一个递归函数 lcs(i, j),其中 ij 表示序列 A 和 B 中当前考虑的元素索引。
  2. 如果 ij 超出序列长度,返回 0。
  3. 如果 A[i] == B[j],返回 lcs(i+1, j+1) + 1
  4. 否则,返回 max(lcs(i+1, j), lcs(i, j+1))

自底向上解法:

  1. 创建一个二维表 dp,其中 dp[i][j] 表示序列 A 的前 i 个元素和序列 B 的前 j 个元素的最长公共子序列长度。
  2. 初始化 dp 表的第一行和第一列为 0。
  3. 对于 i 从 1 到序列 A 的长度,对于 j 从 1 到序列 B 的长度,计算 dp[i][j]
    • 如果 A[i] == B[j]dp[i][j] = dp[i-1][j-1] + 1
    • 否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  4. 返回 dp[m][n],其中 mn 分别是序列 A 和 B 的长度。

结语

动态规划是一门强大的算法技术,它能够高效解决一系列重叠子问题的复杂问题。通过自顶向下和自底向上的策略,以及备忘录的妙用,动态规划算法在实践中发挥着至关重要的作用。