Dynamic Programming

发布时间 2023-11-14 11:26:56作者: dctwan


参考:https://cloud.tencent.com/developer/article/1692068

热身

斐波那契数列

递归求解

自顶向下,存在大量的重复计算

image-20231111090244992

动态规划

保存中间状态,利用保存的历史状态求解问题,减少了重复计算,空间换时间。

image-20231111090434541

空间复杂度优化

只需借助两个变量dp1、dp2

image-20231111091942235

解题步骤:

步骤一:定义dp数组的含义
步骤二:定义状态转移方程
步骤三:初始化过程转移的初始值
步骤四:可优化点(可选)

198. 打家劫舍

  1. dp[i]:到第i户时,抢得最大金额

  2. 状态转移方程

    • 偷第i户

      不能连着偷,dp[i] = dp[i-2] + nums[i-1],此处用i-1是因为第i户,抢得金额刚好对应的nums[i-1]

    • 不偷第i户

      dp[i] = dp[i-1]

    综上

    dp[i] =  max(dp[i-2] + nums[i-1], dp[i-1])
    
  3. 初始值

    dp[0] = 0			// 还没开始偷
    dp[1] = nums[0]		// 到了第1户直接偷
    

62. 不同路径

  1. dp(i,j)

    走到(i,j)处的不同路径数

  2. 状态转移方程

    只能向下或向右走,到达(i,j)位置,要么从上面的位置(i-1,j)处来,要么从左边得位置(i,j-1)处来

    dp(i,j) = dp(i-1,j) + dp(i,j-1)

  3. 初始值

    dp(0,0) = 1

    第一行的所有位置(0,i),只能由起始点向右走,只有一种路径;同理,第一列的位置(i,0),只能由起始点向下走,也都只有一种路径。

63. 不同路径 II

路径上有障碍物

  1. dp(i,j)

    走到(i,j)处的不同路径数

  2. 状态转移方程

    • 没有障碍物的情况下

      只能向下或向右走,到达(i,j)位置,要么从上面的位置(i-1,j)处来,要么从左边的位置(i,j-1)处来

      dp(i,j) = dp(i-1,j) + dp(i,j-1)

    • 有障碍物

      dp(i,j) = 0

  3. 初始值

    第一行的所有位置,只要遇到一个障碍物,其后的所有都不可达,均置为0;第一列同理。

213. 打家劫舍 II

环形打家劫舍,首户和尾户不能同时抢

思路:

  • 只有1户,直接抢即可

  • 只有2户,抢最大金额即可

  • 超过2户,需要考虑两者情况(抢首户 or 抢尾户),再取较大者

  1. dp[i]

    表示抢第i户所得最大金额

  2. 状态转移方程

    同打家劫舍Ⅰ

  3. 边界条件

    dp[0] = 0;

    dp[1] = nums[0]; //抢首户不抢尾户

    dp[1] = nums[1]; //抢尾户不抢首户

337. 打家劫舍 III

子问题

树中每个节点都有 不选 两种选择。子问题subproblem定义为,子树根节点的抢得最大金额 和 子树根节点不选的抢得最大金额;原问题的解就是 max(sub(根节点选), sub(根节点不选))

c++ 实现,二叉树后序遍历,用2个hash表存储,选或不选每个节点的子问题的解

定义f表存储选子树根节点的最优解,g表存储不选子树根节点的最优解

状态转移方程

  • f[t] = t->val + g[t->left] + g[t->right]

    选子树根节点,则左右子节点均不能选

  • g[t] = max(f[t->left], g[t->left]) + max(f[t->right], g[t->right])

    不选子树根节点,左子树根节点可选可不选,取最大值;右子树同理