动态规划

是什么

它将一个问题分解为相互重叠的子问题,能过反复求解子问题,来解决原来的问题

与分而治之的区别

子问题是否重叠

demo:翻转树 vs 斐波哪契数列

翻转树: 拆天2棵树,左右树再翻转,是不样的的操作

斐波哪契数列: fn(n)= fn(n-1)+fn(n-2),适用于所有子问题

leetcode

斐波哪契数列

    // function test(n){
    //   if(n <=1){
    //     return 1;
    //   }
    //   return test(n) = test(n-1)+test(n-2)
    // }

    function test(n){
      if(n ==0){
        return 0;
      }
      if(n ==1){
        return 1
      }
      return test(n-1)+test(n-2)
    }
    var result = test(3)
    console.log('result',result);

70 爬楼梯

思路

  1. 假设要爬到第n级楼梯,哪么倒退一步有2种方案, 第n-1级楼梯+1,第n-2级楼梯+2
  2. 首先考虑第一步的走法:第一步可以走1级台阶,也可以走2级台阶。那么,设S(n)表示走n级台阶的走法数量,如果第一步走1级台阶,剩下的台阶数为n-1,也就是说这种情况下的走法是相当于S(n-1);同理,如果第一步走2级台阶,剩下的台阶数为n-2,这样的走法相当于S(n-2)

代码

基础版

  if (n < 2) {
    return 1
  }
  const db = [1,1]
  for (let i = 2; i <= n; i++) {
    db[i] = db[i-1] + db[i-2]
  }
  console.log(db);
  return db[db.length-1]

优化,缓存版

var climbStairs = function(n) {
  if (n < 2) {
    return 1
  }
  let db0 = 1;
  let db1 = 1;
  for (let i = 2; i <= n; i++) {
    const temp = db0;
    db0 = db1;
    db1 = db1 + temp;
  }
  console.log(db1);
  return db1
};

198 打家劫舍

思路

  1. 求出特殊情况(nums.length === 0,nums.length === 0)
  2. 除了特殊情况外的通用公式dp[i] = Math.max(nums[i]+dp[i-2],dp[i-1])

322 零钱兑换

题目

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 - 1。
你可以认为每种硬币的数量是无限的。

输入: var coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1

思路

我们经常听到「最优子结构」「缩小问题规模」「自顶向下」「自底向上」等跟动态规划相关的词汇

这里我们使用「自顶向下」思想来考虑这个题目

  • 假设给出的不同面额的硬币是[1, 2, 5],目标是 120,问最少需要的硬币个数?
  • 我们要分解子问题,分层级找最优子结构,看到这又要晕了哈,憋急~~ 下面马上举例。
  • 这里我们使用「自顶向下」思想来考虑这个题目,然后用「自底向上」的方法来解题,
    体验算法的冰火两重天。
  • dp[i]: 表示总金额为 i 的时候最优解法的硬币数
  • 我们想一下:求总金额 120 有几种方法?下面这个思路关键了 !!!
    一共有 3 种方式,因为我们有 3 种不同面值的硬币。

    1. 拿一枚面值为 1 的硬币 + 总金额为 119 的最优解法的硬币数量
      这里我们只需要假设总金额为 119 的最优解法的硬币数有人已经帮我们算好了,
      不需要纠结于此。(虽然一会也是我们自己算,哈哈)
      即:dp[119] + 1
    2. 拿一枚面值为 2 的硬币 + 总金额为 118 的最优解法的硬币数
      这里我们只需要假设总金额为 118 的最优解法的硬币数有人已经帮我们算好了
      即:dp[118] + 1
    3. 拿一枚面值为 5 的硬币 + 总金额为 115 的最优解法的硬币数
      这里我们只需要假设总金额为 115 的最优解法的硬币数有人已经帮我们算好了
      即:dp[115] + 1
    • 所以,总金额为 120 的最优解法就是上面这三种解法中最优的一种,也就是硬币数最少
      的一种,我们下面试着用代码来表示一下:
    • dp[120] = Math.min(dp[119] + 1, dp[118] + 1, dp[115] + 1);
    • 推导出「状态转移方程」:
      dp[i] = Math.min(dp[i - coin] + 1, dp[i - coin] + 1, ...)
      其中 coin 有多少种可能,我们就需要比较多少次,那么我们到底需要比较多少次呢?
      当然是 coins 数组中有几种不同面值的硬币,就是多少次了~ 遍历 coins 数组,
      分别去对比即可
    • 上面方程中的 dp[119],dp[118],dp[115] 我们继续用这种思想去分解,
      这就是动态规划了,把这种思想,思考问题的方式理解了,这一类型的题目
      问题都不会太大。 -->

跳跃游戏


渣渣辉
1.3k 声望147 粉丝