「算法修炼」 Leetcode - 动态规划

188 阅读3分钟

Keep coding , Keep learning .

一、基本概念

动态规划(Dynamic Programming),简称DP,是一种常用的算法思想,它的核心思想是将原问题分解成若干个子问题,通过解决子问题的最优解来得到原问题的最优解。动态规划算法通常用于求解具有重叠子问题和最优子结构性质的问题。

动态规划五步(代码随想录):

  1. 确定dp数组以及下标的含义
  2. 确定递推公式(状态转移方程)
  3. dp数组如何初始化,寻找边界
  4. 确定遍历顺序
  5. 举例推导dp数组

二、例题

1. 斐波那契数

leetcode:509.斐波那契数 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0 , F(1) = 1 F(n) = F(n -1) + F(n - 2),其中n > 1 给定 n ,请计算 F(n)

  1. 确定dp数组以及下标的含义

    在这道题目里,dp[i]的含义是指第i个数的斐波那契数值。

  2. 确定递推公式(状态转移公式)

    递推公式就是题目中给出的F(n) = F(n -1) + F(n - 2)

    换个写法就是 dp[i] = dp[i -1] + dp[i - 2]

  3. dp数组如何初始化

    如何初始化,也就是寻找边界值。

    题目已给出边界值F(0) = 0 , F(1) = 1d[0] = 0 , d[1] = 1

  4. 确定遍历顺序

    从递归公式dp[i] = dp[i - 1] + dp[i - 2]中可以看出,dp[i]是依赖 dp[i - 1] dp[i - 2],后一项依赖前两项,那么遍历的顺序一定是从前到后遍历。

  5. 举例推导dp数组

    此处举例当n = 8 时,即打印的dp数组应该是 [0,1,1,2,3,4,8,13,21]

    当代码运行结果不对时,就把dp数组打印出来,与我们推导的数列对比,看是否一致。

JavaScript代码

/**
 * @param {number} n
 * @return {number}
 */
var fib = function(n) {
    // 初始值 => dp[0] = 0 ; dp[1] = 1  
    let dp = [0,1]
    // for循环从2开始,n要取等号
    for(let i = 2 ;i <= n ;i++){
      // 递推公式(状态转移方程)
        dp[i] = dp[ i - 1] + dp[i - 2]
    }
    return dp[n]
};

2. 爬楼梯

leetcode:70.爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

  1. 确定dp数组以及下标的含义

  2. 确定递推公式(状态转移方程)

    为了简化理解,先将n改成10,每次可以爬12个台阶,一共爬10阶有多少种方法。

    • 当只差一步到达第10个台阶时,此时有两种情况:①从第9个台阶爬1个到达10,②也可以从第8个台阶爬2个到达10
    • 假设从1阶到9阶有X种方法,从1阶到8阶有Y种方法,则从1阶到10阶有X+Y种方法
    • 递推公式 dp[i] = dp[i - 1] + dp[i - 2]
  3. dp数组如何初始化,寻找边界

    1阶和2阶,F(1)F(2)时我们可以直接得出答案,有1种和2种,即 d[1] = 1 , d[2] = 2

  4. 确定遍历顺序

    从递归公式dp[i] = dp[i - 1] + dp[i - 2]中可以看出,dp[i]是依赖 dp[i - 1] dp[i - 2],后一项依赖前两项,那么遍历的顺序一定是从前到后遍历。

  5. 举例推导dp数组

    此处举例当n = 5 时,即打印的dp数组应该是 [X,1,2,3,5,8](dp[0]在本题无意义)

    当代码运行结果不对时,就把dp数组打印出来,与我们推导的数列对比,看是否一致。

JavaScript代码

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    // dp[0]无意义,可以是任意值,for循环从3开始
    const dp = [0,1,2] 
    for(let i = 3 ; i <= n; i++){
        dp[i] = dp[i - 1] + dp[i-2]
    }
    return dp[n]
};