【代码随想录】动态规划1-基础
wbfwonderful Lv4

动态规划

动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。

解题步骤:

  • 确定 dp 数组(dp table)以及下标的含义
  • 确定递推公式
  • dp 数组如何初始化
  • 确定遍历顺序
  • 举例推导 dp 数组

509. 斐波那契数

link
斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1, F(n) = F(n - 1) + F(n - 2),其中 n > 1。给你 n ,请计算 F(n) 。

思路

  • 确定 dp 数组(dp table)以及下标的含义:第 i 个数的斐波那契数值是 dp[i]
  • 确定递推公式:题目中已经告知 dp[i] = dp[i - 1] + dp[i - 2]
  • dp 数组如何初始化:题目中也告知 dp[0] = 0, dp[1] = 1
  • 确定遍历顺序:从递归公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到后遍历的
  • 举例推导 dp 数组:
1
2
3
4
5
6
7
8
class Solution:
def fib(self, n: int) -> int:
res = [0, 1]

for i in range(2, n + 1):
res.append(res[i - 1] + res[i - 2])

return res[n]

70. 爬楼梯

link

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

思路

  • 确定 dp 数组(dp table)以及下标的含义:到达第 i 阶的走法为 dp[i]
  • 确定递推公式:可以定义为 dp[i] = dp[i - 1] + dp[i - 2](dp[i - 1] 走一步,dp[i - 2] 走两步)
  • dp 数组如何初始化:第 0 阶不走,可以定义为 0(dp[0] = 0),第一阶只有 1 种走法(dp[1] = 1),第二阶有 2 种(dp[2] = 2)
  • 确定遍历顺序:从前往后
  • 举例推导 dp 数组:
1
2
3
4
5
6
7
8
class Solution:
def climbStairs(self, n: int) -> int:
res = [0, 1, 2]

for i in range(3, n + 1):
res.append(res[i - 1] + res[i - 2])

return res[n]

746. 使用最小花费爬楼梯

link
给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。

思路

  • 确定 dp 数组(dp table)以及下标的含义:到达第 i 阶的最小花费
  • 确定递推公式:可以定义为 dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])(dp[i - 1] 走一步,dp[i - 2] 走两步)
  • dp 数组如何初始化:题目已经给出,可以从 0 或者 1 开始走,说明 dp[0] dp[1] 都是 0
  • 确定遍历顺序:从前往后
  • 举例推导 dp 数组:
1
2
3
4
5
6
7
8
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
res = 0
dp = [0, 0]
for i in range(2, len(cost) + 1):
dp.append(min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]))

return dp[len(cost)]

62.不同路径

link

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

思路

  • 确定 dp 数组(dp table)以及下标的含义:到达第 (i, j) 位置的方法数
  • 确定递推公式:可以定义为 dp[i][j] = dp[i-1][j] + dp[i][j-1],要么从左边来,要么从上面来。注意考虑边界情况
  • dp 数组如何初始化:可以定义为 1,这样 dp[0][1] 和 dp[1][0] 就可以使用递推公式来计算了
  • 确定遍历顺序:从前往后
  • 举例推导 dp 数组:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[0] * n for _ in range(m)]
dp[0][0] = 1
for i in range(m):
for j in range(n):
# 第一行
if i == 0:
if j != 0:
dp[i][j] = dp[i][j - 1]
# 第一列
elif j == 0:
if i != 0:
dp[i][j] = dp[i - 1][j]
# 剩余情况
else:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

return dp[m - 1][n - 1]

其实第一行(dp[0][:])和第一列(dp[:][0])只能为 1,所以可以从 1 开始遍历

数论思路(高中数学 😂)

一共需要 m - 1 个竖着走和 n - 1 个横着走,总共是 m + n - 2 个步骤。相当于是给 m + n - 2 个不同的数,随便取 m - 1 个数,有几种取法。

63. 不同路径 II

343. 整数拆分

96.不同的二叉搜索树