佛系分析算法(动态规划),是兄弟就来砍(点赞)我

455 阅读3分钟

hello 大家好我是django,今天想跟大家分享一下关于动态规划算法的解题思路和方法。其中文章会结合leecode的题目给大家理解

  • 在我的理解动态规划说白了就是将上一次的结果缓存起来,在下一次的计算中进行复用。说难也不难说简单也不简单,类似于递归一样也需要采用数学归纳法进行思考(一般会采用一位数组或二维数组进行数据缓存)

动态规划三要素

定义数组元素的含义

例如我们假设数组dp[i]是我们想要求解的值,那么这个数组的含义就被我们定义好了。就是表示第i项时我们需要的答案为dp[i]

找到数组之间的关系

动态规划核心在于是使用之前的缓存值进行下一次计算,所以肯定存在某种关联关系dp[i] = dp[i - 1]...dp[i - 2](往往这一步是最难以想到的)

初始化边界值

就像递归算法一样,在满足某个条件下一定要有返回,否者将会无限调用下去造成死循环,那么动态规划也是一样需要考虑边界问题就例如dp[i] = dp[i - 1]...dp[i - 2]这个表达式当i = 1 or i = 0时是不是就会出现问题了,数组下标将会变成负数。这显然不是我们想要的,所以初始化边界值是动态规划的关键所在。

我们按照上面所说的三要素,来做一做leecode上的题目加深一下三要素的理解

一、青蛙上楼(动态规划)

这道题在leecode上应该是一道简单难度的题目,先给大家小试牛刀找找自信

  • 要求

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

  • 思路
  1. 定义 dp[i]为青蛙达到第i层所有的路径(第一要素)
  2. 找到关系dp[i] = dp[i - 1] + dp[i - 2] 确定边界值当i > 2 等式成立(第二要素)
  3. 找到初始值,并初始化。当i <= 2dp[i] = i (第三要素) 可以看到我们完全符合了动态规划三要素的要求
  • 实现
function frogUpstair(n) {
  // 定义dp[i]为青蛙到达第i层有dp[i]种方法
  // 为什么需要创建n+1个大小的数组?
  // 因为需要算第n层 0~n 所以数组大小为 n + 1
  const dp = new Array(n + 1).fill(0);
  for (let i = 0; i < n; i++) {
    if (n <= 2) dp[i] = i;
    else {
      dp[i] = dp[i - 1] + dp[i - 2];
    }
  }
  return dp[n];
}

很明显这道题其实就是斐波那契数列数列的变种题目,难度稍微比较低

二、不同路径(动态规划)

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径? 图片

示例 1:

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

提示:

1 <= m, n <= 100
题目数据保证答案小于等于 2 * 109
  • 思考
  1. 定义dp[m][n]为移动到m,n总共有dp[m][n]种方法
  2. 找到元素关系:当m >=1, n >= 1dp[m][n] = dp[m - 1][n] + dp[m][n - 1]
  3. 确认初始值:当m = 0, n = 0 dp[0][n] = 1, dp[m][0] = 1
  • 实现
/**
 * @param {number} m
 * @param {number} n
 * @return {number}
 */
var uniquePaths = function(m, n) {
    // 定义 dp[i] 的意义
    const dp = new Array(m).fill(0).map(() => new Array(n));

    // 找到初始化的值
    for(let i = 0; i < m; i++) {
        dp[i][0] = 1
    }
     // 找到初始化的值
    for(let i = 0; i < n; i++) {
        dp[0][i] = 1
    }

    for(let i = 1; i < m; i++) {
        for(let j = 1; j < n; j++) {
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        }
    }

    return dp[m - 1][n - 1];
};

从上面两道题目中我们可以看到,我使用三要素可以比较轻松的找到解题的思路。但是有时候关于元素中的关系是不那么容易找的,但是肯定存在着某种关系。并不是说掌握了三要素对于动态规划就无敌了,算法还是得多刷多练。