LeetCode62.不同路径
题目描述:
一个机器人位于一个 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
解题思路:
机器人从(0,0)出发,到达终点(m-1,n-1),使用动态规划五部曲
1.确定dp数组以及其下标的含义
题目中有m,n两个维度的移动,所以需要创建一个二维数组dp[i][j],表示从(0,0)到(i,j)有几条路径可以到达
2.确定递推公式
因为题目中明确规定了,机器人只能向下或者向右移动,所以dp[i][j]由dp[i-1][j]和dp[i][j-1]推导得到
也就是说可以列出dp[i][j] = d[i-1][j]+dp[i][j-1]
3.dp数组如何初始化
因为dp[i][j]均由dp[i][j-1]和dp[i-1][j]推导而来,所以可知我们只需要将dp[i][0]以及dp[0][j]均初始化为1即可顺利推导
4.确定遍历顺序
因为机器只能向下或向右移动,所以遍历顺序即为从上到下,从左到右
5.举例推导dp数组
如图:
由以上可以得出代码为:
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m,vector<int>(n,0));
for(int i = 0;i < m;i++) dp[i][0] = 1;
for(int j = 0;j < n;j++) dp[0][j] = 1;
for(int i = 1;i < m;i++){
for(int j = 1;j < n;j++){
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
};
·时间复杂度:O(m * n)
·空间复杂度:O(m * n)
总结:依旧是动态规划五部曲,一定要使用五部曲才能保证精准无误,与之前的题目相比,这次我们就要考虑如何正确的初始化了,难度在初步递增
LeetCode63.不同路径II
题目描述:
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1
和 0
来表示。
示例 1:
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2
条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
示例 2:
输入:obstacleGrid = [[0,1],[0,0]] 输出:1
解题思路:
1.确定dp数组以及其下标的含义
与上题一样,使用二维数组dp[i][j],含义为(0,0) 到(i,j)有几条路径可以到达
2.确定递推公式
递推公式并没有变化,但是增加了一个约束条件,当obs[i][j]==1时说明遇到障碍,也就是没有障碍的时候再执行递推公式
3.dp数组如何初始化
最上边与最左边的数初始化为1,但是如果遇到障碍,则需要直接跳过,并且之后的值均为0
vector<vector<int>> dp(m, vector<int>(n, 0)); // 初始值为0
for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int j = 0; j < n; j++) dp[0][j] = 1;
4.确定遍历顺序
按照题目要求,从左到右,从上到下遍历,遇到障碍需要将障碍物跳过
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (obstacleGrid[i][j] == 1) continue;
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
5.举例推导dp数组
以示例1为例,如图:
分析完毕,代码如下:
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
if(obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1) return 0;//如果在起始位置,或者终点位置存在障碍,则没有路径到达
vector<vector<int>> dp(m,vector<int>(n,0));
for(int i = 0;i < m && obstacleGrid[i][0] == 0;i++) dp[i][0] = 1;
for(int j = 0;j < n && obstacleGrid[0][j] == 0;j++) dp[0][j] = 1;
for(int i = 1;i < m;i++){
for(int j = 1;j < n;j++){
if(obstacleGrid[i][j] == 1) continue;
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
};
·时间复杂度:O(n*m)
·空间复杂度:O(n*m)
总结:乍一看以为,与上一题好像没有什么区别,但是如果没有仔细分析,直接上手,还是会有很多会出错的地方,其实只需要考虑到,遇到障碍dp[][j]保持0即可
还有初始化时,障碍之后都应该是0