动态规划基础和框架。
动态规划核心,当前每一个状态一定是由上一个状态推导出来,这就涉及到一个递推公式,但是动态规划又不仅仅只是递推公式。动态规划分为五部曲
1. 你要明白动态dp数组的值的含义。dp[i]或者dp[i][j]代表什么
2. dp数组的递推公式是什么
3. dp数组如何初始化
4. 遍历顺序是什么
5. 举例推到dp数组
509 斐波拉契 数列
斐波那契数 (通常用 F(n)
表示)形成的序列称为 斐波那契数列 。该数列由 0
和 1
开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n
,请计算 F(n)
。
这个虽然很简单,简单在于递推公式直接给你了,而且初始化也告诉你了
这里dp[i]的每个值就是F(i)的值,递推公司已经给了,初始化也给了。
遍历顺序就是从左到右,单层遍历。
def Fibo(n:int):
dp = [0] * (n+1)
dp[0] = 0
dp[1] = 0
for i in range(2, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
70 爬楼梯
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
这个题没做过的,直接懵逼。做过的,动态规划就可以一步一步出来。
这个题是需要通过前面的状态来知道你下一步状态的。为什么? 因为你走一步,要么是前一步跨一个台阶,要么前一步跨两个。这就是方法的数量。
因此采用动态规划,那么五部曲
1. dp[i] 的含义是什么? dp[i] 就是走到这一步台阶的方法总数
2. 递推公式是啥?首先,你走到每一步,不外部是前一步走一步到的,或者前一步走两步到的。因为,你到当前步的方数数量就等于dp[i-1] 走一步 加上dp[i-2]走两步的方法数。即dp[i] = dp[i-2] + dp[i-1]
3. 初始化.dp[1] = 1 dp[2] = 1. dp[0] 不用赋值,没有意义。因为台阶n>=1
4. 如何遍历? 从左往右遍历
5. 测试 dp[3] 走到第三步台阶有多少方法,第一个台阶走两步,第二个台阶走一步。因此为2符合这个递推公式
def climbStairs(n: int):
dp = [0] * (n+1)
dp[1] = 1
dp[2] = 1
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
746 最小花费爬楼梯
给你一个整数数组 cost
,其中 cost[i]
是从楼梯第 i
个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0
或下标为 1
的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
同样的,这个题也是五部曲。
1. dp的含义,dp[i] 就是达到第i个楼梯的总花费
2. 递推公式。与前面爬楼类似,你要么从前一步爬,要么从前两步爬,但是你要选择消耗最小的
dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2])
3. dp初始化,因为刚开始第一步和第二步楼梯可以初始化为0 dp[0] = 0, dp[1] = 0,因为我还没开始爬
4. 遍历顺序,从左往右
5. 测试。
def stairs2(cost:List[int]):
n = len(cost)
dp = [0] * (n+1)
dp[0] = 0
dp[1] = 0
for i in range(2, n+1):
dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2])
return dp[n]