动态规划理论基础
动态规划:每一个状态一定是由上一个状态推导出来的。
贪心:局部直接选最优的
解题步骤
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
509. 斐波那契数
题目链接:509. 斐波那契数 - 力扣(LeetCode)
思路
- dp[i]的定义为:第i个数的斐波那契数值是dp[i]。
- 递推公式: 题目已经给出 dp[n] = dp[n-1] + dp[n-2]。
- dp[0] = 0, dp[1] = 1。
- dp[n]是依赖dp[n-1]和dp[n-2]的,所以应从前往后遍历。
- n从0到10的斐波那契值:0 1 1 2 3 5 8 13 21 34 55
代码实现
class Solution(object):def fib(self, n):if n <= 1:return ndp = [0, 1]for i in range(n):total = dp[0] + dp[1]dp[0] = dp[1]dp[1] = totalreturn dp[0]
在for循环中,我用了range(n), 而代码随想录里是range(2, n+1), 在这个循环中,不直接用到i,只是表明循环的次数,所以可以不一样。我相当于让它循环了n次,所以最终取dp数组中第一个数,而代码随想录循环了n-1次,最终取数组中第二个数。
也可用递归法求解
class Solution(object):def fib(self, n):if n <= 1:return nreturn self.fib(n-1) + self.fib(n-2)
70. 爬楼梯
题目链接:70. 爬楼梯 - 力扣(LeetCode)
思路
到三层可以从二层爬一个阶梯,也可以从一层爬两个阶梯,因此到当前层的方法到第三层楼梯的状态可以由第二层楼梯 和 到第一层楼梯状态推导出来。
- dp[i]: 到达i层总共有dp[i]种方法。
- 可以用dp[i-2]种方法到达i-2层,再爬两层到达i层,也可以用dp[i-1]种方法到达i-1层,再爬一层到达i层,因此到达i层的方法数为到前一层和前两层的方法之和。dp[i] = dp[i-2] + dp[i-1].
- i为1时无意义,不考虑。dp[1] = 1, dp[2] = 2
- dp[i]依赖dp[i-2] 和 dp[i-1]的值,所以从前往后遍历。
- i为1-5:dp[i]为 1 2 3 5 8
代码实现
class Solution(object):def climbStairs(self, n):if n <= 2:return ndp = [1, 2]for i in range(3,n+1):total = dp[0] + dp[1]dp[0] = dp[1]dp[1] = totalreturn dp[1]
for循环用range(3, n+1)比较清楚,表明i要从3开始计算,一直到n为止。
746. 使用最小花费爬楼梯
题目链接:746. 使用最小花费爬楼梯 - 力扣(LeetCode)
思路
- dp[i]: 到达i层花的最小力气。
- 可以花dp[i-2]到达i-2层,再花费cost[i-2]到达i层,也可以花dp[i-1]到达i-1层,再花费cost[i-1]到达i-2层,那么到达i层的最小花费就是这两种方法的花费最小的力气,即dp[i] = min(dp[i-2] + cost[i-2], dp[i-1] + cost[i-1])
- 到达0层和1层的花费为0, dp[0] = dp[1] = 0.
- 从前向后遍历。
- 跟据cost数据具体分析。
代码实现
class Solution(object):def minCostClimbingStairs(self, cost):if len(cost) <= 1:return 0dp = [0, 0]for i in range(2, len(cost)+1):minCost = min(dp[0]+cost[i-2], dp[1] + cost[i-1])dp[0] = dp[1]dp[1] = minCostreturn dp[1]