Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
接着上一篇,我们继续刷动态规划,在上一篇中,我们将动态规划刷题分解成4个部分:
- 确定状态
- 状态转移方程
- 初始条件和边界情况
- 计算顺序
- 编码实现
那么我们稍后仍是按照这个解题步骤去解题,本次的题目是 leetcode 的 👉 62. 不同路径,这个题目有一点小小的升级,就是会使用二位数组来解题。
题目
描述
首先我们看一下题目描述:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例
输入:m = 3, n = 7
输出:28
也就是说我们从(0, 0)位置走到(2, 6)这个位置总共有28种不同的路径
开始解题
一、确定状态
在这道题中,我们需要开辟一个二维数组,二维数组中的每个元素 代表着我们的状态,即f[i][j] = 能从(0, 0)到达(i, j)点的路径数量。
确定状态需要两点:
- 最后一步
- 子问题
最后一步
无论机器人🤖️用怎样的样式到达右下角,总有挪动的最后一步,这一步一定是向右👉或者向下👇挪动
我们根据示例可知,右下角的坐标为
那么前一步一定在
子问题
我们已知机器人🤖️有 种方式从左上角(0, 0)走到(m - 2, n - 1)
有 种方式从左上角(0, 0)走到(m - 1, n - 2)
则机器人🤖️有 种方式从左上角走到(i, j),即
二、状态转移方程
我们就可以对于任意一个格子(i, j)确立方程:
解释:
f[i][j]:从左上角 (0, 0) 走到 (i, j) 的路径数量
f[i - 1][j]:从左上角 (0, 0) 走到 (i - 1, j) 的路径数量,即 (i, j) 上面的一个格子 f[i][j - 1]:从左上角 (0, 0) 走到 (i, j - 1) 的路径数量,即 (i, j) 左边的一个格子
三、初始条件和边界情况
初始条件
我们考虑初始条件(0, 0),这个点只有一种方式到达,即 f[0][0] = 1
边界情况
当 i = 0 或 j = 0 时,这两种情况都只有 1 个方向能过来,即这两种情况下的 f[i][j] = 1
上面两个点中,边界情况已经包含了初始条件,所以我们在编码时可以不用进行初始条件的初始化了
四、计算顺序
这个题我们需要按照一行接一行的顺序来计算,这样更符合我们正常的思维方式
计算第 0 行:f[0][0], f[0][1], ..., f[0][n - 1]
计算第 1 行:f[1][0], f[1][1], ..., f[1][n - 1]
🥳🥳🥳 我是省略号 🥳🥳🥳
计算第 m - 1 行:f[m - 1][0], f[m - 1][1], ..., f[m - 1][n - 1]
此题第答案为:f[m - 1][n - 1]
同时我们也可以根据上述解题思路得知解本题的时间复杂度与空间复杂度均为
五、编码
function uniquePaths(m: number, n: number): number {
let dp = [] // 1. 创建数组
for (let i = 0; i < m; i++) {
// 3. 每一行一开始初始化为一个数组,即填补 dp 为一个二维数组
dp[i] = []
for (let j = 0; j < n; j++) {
if (i === 0 || j === 0) {
// 初始值 f[0][0] 与 边界条件 i 或 j 为 0 时 dp[i][j] = 1
dp[i][j] = 1
} else {
// 直接使用状态转移方程即可
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
}
}
}
// 2. 直接返回答案
return dp[m - 1][n - 1]
}
至此,我们完成了第二个 dp 问题,再总结一边 dp 的刷题套路:
-
确定状态
a) 最后一步
b) 子问题 -
状态转移方程
-
初始条件与边界情况
-
计算顺序
最后根据得出的状态转移方程以及需要考虑的初始条件与边界条件进行编码即可。
动态规划篇仍会继续更新,希望各位plmm与sqgg能给点个赞,在留言区给点反馈,感谢🙏