代码随想录算法训练营Day38 | 动态规划(1/17) 理论基础 LeetCode 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

来到了练习的后期了,动态规划也是内容最多的一块,包括基础题目、背包问题、打家劫舍、股票问题和子序列问题这几部分。

1. 动态规划的理论基础

1.1 什么是动态规划

动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。

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

1.2 动态规划的解题步骤

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

看到这里,感觉dp有点像Markov property的意思,当一个随机过程在给定现在状态及所有过去状态情况下,其未来状态的条件概率分布仅依赖于当前状态。尽管这么说有一点牵强,不过都是前一步影响后一步的迭代过程。

1.3 动态规划应该如何debug

做动规的题目,写代码之前一定要把状态转移在dp数组的上具体情况模拟一遍,心中有数,确定最后推出的是想要的结果。

然后再写代码,如果代码没通过就打印dp数组,看看是不是和自己预先推导的哪里不一样。

  • 如果打印出来和自己预先模拟推导是一样的,那么就是自己的递归公式、初始化或者遍历顺序有问题了。
  • 如果和自己预先模拟推导的不一样,那么就是代码实现细节有问题。

2. 练习题

第一题

509. Fibonacci Number

The Fibonacci numbers, commonly denoted F(n) form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. That is,

F(0) = 0, F(1) = 1
F(n) = F(n - 1) + F(n - 2), for n > 1.

Given n, calculate F(n).

这是一道简单题,因为递推公式已经给我们了:F(n) = F(n - 1) + F(n - 2),初始化也有了:F(0) = 0, F(1) = 1

class Solution:
    def fib(self, n: int) -> int:
        if n == 0:
            return 0
        dp = [0] * (n + 1)
        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. Climbing Stairs

You are climbing a staircase. It takes n steps to reach the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

第三阶台阶可以由前两阶台阶的不同走法递推出来,后面的台阶也一样,因此这也是动态规划解决的问题。而且把前5阶的结果推理出来,会发现这仍然是上一题的Fibonacci numbers。

class Solution:
    def climbStairs(self, n: int) -> int:
        if n <= 1:
            return n
        
        dp = [0] * (n + 1)
        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. Min Cost Climbing Stairs

You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps.

You can either start from the step with index 0, or the step with index 1.

Return the minimum cost to reach the top of the floor.

dp[i - 1] 跳到 dp[i] 需要花费 dp[i - 1] + cost[i - 1]。

dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。

为了让花费最小,要选择dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);

这道题的思路就出来了。

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

你可能感兴趣的:(LeetCode,算法,动态规划,leetcode)