「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。
题目
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例1
输入:m = 3, n = 7
输出:28
示例2
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下
思路
DFS
暴力解法
-
定义一个递归函数
jump
,接受当前机器人的位置坐标[x, y]
-
每次递归,做如下判断
-
如果机器人到达终点
x == m && y == n
,将结果count++
, 同时结束递归 -
如果当前坐标
x < m
,继续以参数[x + 1, y]
执行递归函数 -
如果当前坐标
y < n
,继续以参数[x, y + 1]
执行递归函数
-
-
机器人初始坐标为
[1, 1]
,启动递归函数 -
返回
count
即为所求
代码如下
/**
* @param {number} m
* @param {number} n
* @return {number}
*/
var uniquePaths = function(m, n) {
let count = 0;
const jump = ([x, y]) => {
if(x == m && y == n) {
count++;
return;
}
if(x < m) {
jump([x + 1, y])
}
if(y < n) {
jump([x, y + 1])
}
}
jump([1, 1]);
return count;
};
上述解法的时间复杂度很高,在 LeetCode
提交的时候,不出意外的超时了
动态规划
如果想要用 动态规划
来解决这个问题,我们需要进行以下几个步骤的分析:
-
定义
dp
数组元素的含义 -
确定数组元素间的关系式
-
确定边界、初始条件
对于本题,dp
数组显然是一个二位数组,可以用 dp[i][j]
表示从左上角走到 (i, j)
的路径数量,其中 0 <= i < m, 0 <= j < n
;
那么对于数组间的关系式:对于 (i, j)
,由于题目要求机器人只能向下
或者向右
移动,那么走到 (i, j)
的上一步,只能为 (i - 1, j)
或者 (i, j - 1)
,所以显然有 dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
;
边界就是刚刚指出的 0 <= i < m, 0 <= j < n
,那么初始条件,是什么呢?思考一下就可得,dp[i][0] 都是等于 1 的
,同时 dp[0][j] 都是等于 1 的
。
dp[m - 1][n - 1]
即为题目所求
代码如下
/**
* @param {number} m
* @param {number} n
* @return {number}
*/
var uniquePaths = function(m, n) {
var dp = new Array(m).fill(() => new Array(n).fill(0));
for(let i = 0; i < m; i++) {
dp[i][0] = 1;
}
for(let j = 0; j < n; j++) {
dp[0][j] = 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]
}
小结
暴力解法,可能是最直观的方法,但是当递归的次数增加时,如果不能很好剪枝,就会出现大量的重复计算问题。而对于效率要求较高的算法来说,在某些情况下可能会不太适用,这个时候就需要寻找另外的解法。
但是还是有一点是需要坚持的,暴力解会提供很多思考的方向,像如果存在重复的计算,可以尝试使用缓存已计算的结果,下次遇到时直接从缓存拿,也可能会提升很多效率。
# LeetCode 👉 HOT 100 👉 不同路径 - 中等题 ✅
合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍
如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄