今天开始学动态规划
其实动态规划这个部分一直都挺难的,一般包含:背包问题,打家劫舍,股票问题,子序列问题
对于动态规划问题,拆解为如下五步曲:
动态规划理论基础 | 动态规划 | 状态推导 | dp数组 | 代码随想录
-
确定dp数组(dp table)以及下标的含义
-
确定递推公式
-
dp数组如何初始化
-
确定遍历顺序
-
举例推导dp数组
找问题的最好方式就是把dp数组打印出来,看看究竟是不是按照自己思路推导的
自己思考这三个问题:
-
这道题目我举例推导状态转移公式了么?
-
我打印dp数组的日志了么?
-
打印出来了dp数组和我想的一样么?
509. 斐波那契数
题目链接 509. 斐波那契数 - 力扣(LeetCode)
思路
什么是斐波那契数?
斐波那契数是一个整数序列,其中每个数是前两个数的和。序列通常以 0 和 1 开始,后续数字依次为 1, 2, 3, 5, 8, 13, 21......数学上,斐波那契数列的递推关系为:
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2) (n ≥ 2)
斐波那契数列具有许多有趣的数学特性:
-
黄金分割比:随着 n 增大,F(n+1)/F(n) 趋近于黄金比例 (1 + √5)/2 ≈ 1.618。
-
组合意义:斐波那契数表示用 1×1 和 1×2 的方块铺满 1×n 棋盘的方法数。
-
矩阵表示:可通过矩阵幂运算高效计算斐波那契数。
动态规划思想
1.确定dp数组下标及其含义 dp[i]:第i个斐波那契值为dp[i]
2.递推公式 dp[i]=dp[i-1]+dp[i-2]
3.dp数组如何初始化 dp[0]=0 dp[1]=1
4.遍历顺序 从前往后遍历
5.打印dp数组
提交

python
class Solution:
def fib(self, n: int) -> int:
# 排除 Corner Case
if n == 0:
return 0
# 创建 dp table
dp = [0] * (n + 1)
# 初始化 dp 数组
dp[0] = 0
dp[1] = 1
# 遍历顺序: 由前向后。因为后面要用到前面的状态
for i in range(2, n + 1):
# 确定递归公式/状态转移公式
dp[i] = dp[i - 1] + dp[i - 2]
# 返回答案
return dp[n]
70. 爬楼梯
思路
1.确定dp数组下标及其含义 dp[i]:i个台阶需要dp[i]种方法
2.递推公式 dp[i]=dp[i-1]+dp[i-2](我把n=1,2,3,4推了一下,然后看他们数值之间的关系)
3.dp数组如何初始化 dp[0]=0 dp[1]=1
4.遍历顺序 从前往后遍历
5.打印dp数组
那就是和上个题差不多,只是看起来形式不太一样
提交
但是这里需要注意,初始化应该多初始化一个dp[2]=2
因为他不符合我们的递推式,所以要单独写出来
然后遍历就往后移一位

python
class Solution:
def climbStairs(self, n: int) -> int:
if n==0:
return 0
if n==1:
return 1
dp=[0]*(n+1)
dp[0]=0
dp[1]=1
dp[2]=2
for i in range(3,n+1):
dp[i]=dp[i-1]+dp[i-2]
return dp[n]
746. 使用最小花费爬楼梯
题目链接746. 使用最小花费爬楼梯 - 力扣(LeetCode)
思路
1.确定dp数组下标及其含义 dp[i]:到台阶i所需要的最小费用
2.递推公式
这里到i之后可能爬1阶,也可能2阶
dp[i]=min(dp[i−1], dp[i−2])+cost[i]
含义:
-
要到第
i阶,只能从i-1爬 1 步 或 从i-2爬 2 步 -
取两者中花费更小的,再加上当前台阶的费用
cost[i]
这个递推公式我没想出来
3.dp数组如何初始化 dp[0]=cost[0] dp[1]=cost[1]
4.遍历顺序 从前往后遍历
5.打印dp数组
提交

python
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
n=len(cost)
if n==0:
return 0
if n<=2:
return min(cost)
dp=[0]*(n+1)
cost.append(0)
dp[0]=cost[0]
dp[1]=cost[1]
for i in range(2,n+1):
dp[i]=min(dp[i-1],dp[i-2])+cost[i]
print(dp)
return dp[n]