理论基础
动态规划的应用前提是有重叠子问题,通过子问题状态推导获得问题的最终解。第二次做动态规划的题了,希望这次能对动态规划有更透彻的理解。
LeetCode 509 斐波那契数
题目链接:509. 斐波那契数 - 力扣(Leetcode)
斐波那契数列是经典动态规划入门题,但其实根据题目描述进行模拟也可以解题:
class Solution:
def fib(self, n: int) -> int:
if n == 0:
return 0
dp1, dp2 = 0, 1
for i in range(2, n+1):
dp = dp1 + dp2
dp1 = dp2
dp2 = dp
return dp2
关键是帮助理解怎么进行动态规划相关解题过程。
LeetCode 70 爬楼梯
爬楼梯也是最简单的动态规划之一了,一刷时理解了原理和状态推导,子问题方法数本质上就是斐波那契数列,再看题就自然而然AC了:
class Solution:
def climbStairs(self, n: int) -> int:
if n <= 2:
return n
dp1, dp2 = 1, 2
for i in range(3, n+1):
dp = dp1 + dp2
dp1 = dp2
dp2 = dp
return dp
LeetCode 746 使用最小花费爬楼梯
题目链接:746. 使用最小花费爬楼梯 - 力扣(Leetcode)
一开始做这道题可能会对楼顶这个概念存疑,比如到底哪个下标指楼顶?到楼顶是必须到达那个下标,还是最后一步上楼的范围覆盖楼顶即可?个人理解楼顶是指下标为n的位置(也就是第n阶楼梯,数组下标最多为n-1),和爬楼梯一样,到达第i阶楼梯有两种选择,一是从第i-1阶楼梯跨一步上来,二是从第i-2阶楼梯跨两步上来,要得到最小的cost,就应该选择其中花费较小的方式。dp[i]表示到达第i阶楼梯并且继续从需要的最小花费,只要还没到达楼顶,就还需要从当前台阶往上走,必须花费掉当前的cost[i]。基于当前设定,dp数组初始化的dp1和dp2直接初始化为cost[0]和cost[1](到达0下标和1下标的台阶不需要花费,他们的dp就是从0/1出发所需花费)。
class Solution:
def minCostClimbingStairs(self, cost: List[int]) -> int:
n = len(cost)
if n == 2:
return min(cost[0], cost[1])
dp1, dp2 = cost[0], cost[1]
for i in range(2, n):
dp = min(dp1, dp2) + cost[i]
dp1 = dp2
dp2 = dp
return min(dp1, dp2)