动态规划算法详解与应用

动态规划算法详解与应用

动态规划(Dynamic Programming,简称DP)是一种用于解决最优化问题计数问题的高效算法设计方法。它通过将复杂问题分解为子问题,并存储子问题的解(避免重复计算),最终组合出原问题的解。以下是动态规划的详细讲解:


核心思想

  1. 分治思想:将大问题分解为重叠的子问题。
  2. 记忆化存储:保存子问题的解(通常用数组或哈希表),避免重复计算。
  3. 递推关系:找到子问题与原问题之间的状态转移方程。

适用条件

  1. 重叠子问题:问题可以被分解为多次重复计算的子问题。
    • 例如斐波那契数列:F(n) = F(n-1) + F(n-2)
  2. 最优子结构:问题的最优解包含子问题的最优解。
    • 例如最短路径问题:A→B→C的最短路径包含A→B的最短路径。

动态规划的步骤

  1. 定义状态
    • 明确问题的状态变量(例如dp[i]表示第i个斐波那契数)。
  2. 确定状态转移方程
    • 描述状态之间的关系(例如dp[i] = dp[i-1] + dp[i-2])。
  3. 初始化边界条件
    • 设置初始值(例如dp[0] = 0, dp[1] = 1)。
  4. 选择计算顺序
    • 自顶向下(递归+记忆化)或自底向上(迭代填表)。
  5. 输出结果
    • 从最终状态提取答案(例如dp[n])。

经典问题示例

1. 斐波那契数列
  • 问题:计算第n个斐波那契数。
  • 状态转移方程
    dp[i] = dp[i-1] + dp[i-2]
    
  • 代码实现(自底向上)
    def fib(n):
        dp = [0, 1] + [0] * (n-1)
        for i in range(2, n+1):
            dp[i] = dp[i-1] + dp[i-2]
        return dp[n]
    
2. 背包问题(0-1 Knapsack)
  • 问题:在容量限制下选择物品,使总价值最大。
  • 状态定义
    dp[i][w]表示前i个物品在容量w下的最大价值。
  • 状态转移方程
    dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight[i]] + value[i])
    
3. 最长递增子序列(LIS)
  • 问题:找到数组中最长的严格递增子序列长度。
  • 状态转移方程
    dp[i] = max(dp[j] + 1 for j in range(i) if nums[j] < nums[i])
    

动态规划的两种实现方式

  1. 自顶向下(Top-Down)

    • 递归 + 记忆化(备忘录法),适合子问题较少的情况。
    • 示例(斐波那契数列):
      memo = {0: 0, 1: 1}
      def fib(n):
          if n not in memo:
              memo[n] = fib(n-1) + fib(n-2)
          return memo[n]
      
  2. 自底向上(Bottom-Up)

    • 迭代填表,通常更高效(避免递归开销)。
    • 示例(斐波那契数列见前文)。

常见优化技巧

  1. 空间优化
    • 如果状态转移仅依赖前几个状态,可压缩DP表(例如斐波那契中用两个变量代替数组)。
  2. 状态压缩
    • 用位运算或滚动数组减少空间(例如0-1背包问题的一维数组解法)。
  3. 剪枝
    • 提前终止不必要的计算(例如某些问题中dp[i]达到阈值后停止)。

动态规划 vs 贪心算法

  • 动态规划:考虑所有子问题,保证全局最优。
  • 贪心算法:每一步局部最优,但不一定全局最优(例如部分背包问题可用贪心,0-1背包不行)。

练习题推荐

  1. 爬楼梯(斐波那契变种)
  2. 硬币兑换(完全背包问题)
  3. 编辑距离(二维DP经典)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值