文章目录
- 前置知识
- 62.不同路径
- 题目描述
- 解题思路
- 代码
- 63. 不同路径 II
- 题目描述
- 障碍信息传递法(比较复杂)
- 被障碍物阻挡后直接清空计数法(更简洁)
- 总结
前置知识
参考前文
参考文章:
LeetCode刷题笔记【29】:动态规划专题-1(斐波那契数、爬楼梯、使用最小花费爬楼梯)
62.不同路径
题目描述
LeetCode链接:https://leetcode.cn/problems/unique-paths/description/
解题思路
动态规划: 创建m×n的数组, 对应这个地图, 数组val
表示有几种方法可以走到这一格
最开始, 第一行和第一列val都是1
, 然后依次遍历更新val
每一格的val
是其上和左格子的和
代码
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> map(m, vector<int>(n));
for(int i=0; i<n; i++){
map[0][i] = 1;
}
for(int i=0; i<m; i++){
map[i][0] = 1;
}
for(int i=1; i<m; i++){
for(int j=1; j<n; j++){
map[i][j] = map[i-1][j] + map[i][j-1];
}
}
return map[m-1][n-1];
}
};
63. 不同路径 II
题目描述
LeetCode链接:https://leetcode.cn/problems/unique-paths-ii/description/
障碍信息传递法(比较复杂)
参考<62. 不同路径>
动态规划, 先把石头初始化为INT_MAX
(并且初始化过程中前面一个是INT_MAX
, 那他自己也是INT_MAX
)
递推遍历的过程中加一个判断
① 如果左和上都是INT_MAX
, 那么本位置也是INT_MAX
② 如果上/左有一个是INT_MAX
, 那么val
是另一个非INT_MAX
的
③ 正常递推
class Solution {
private:
int maxNum = INT_MAX;
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
for(int i=0; i<obstacleGrid.size(); ++i){
for(int j=0; j<obstacleGrid[0].size(); ++j){
if(obstacleGrid[i][j]==1)
obstacleGrid[i][j] = maxNum;
}
}
if(obstacleGrid[0][0] != maxNum)
obstacleGrid[0][0] = 1;
for(int i=1; i<obstacleGrid[0].size(); ++i){
if(obstacleGrid[0][i-1]==maxNum)
obstacleGrid[0][i] = maxNum;
if(obstacleGrid[0][i] != maxNum)
obstacleGrid[0][i] = 1;
}
for(int i=1; i<obstacleGrid.size(); ++i){
if(obstacleGrid[i-1][0]==maxNum)
obstacleGrid[i][0] = maxNum;
if(obstacleGrid[i][0] != maxNum)
obstacleGrid[i][0] = 1;
}
for(int i=1; i<obstacleGrid.size(); ++i){
for(int j=1; j<obstacleGrid[0].size(); ++j){
if(obstacleGrid[i][j]==maxNum)
continue;
int left = obstacleGrid[i-1][j];
int over = obstacleGrid[i][j-1];
if(left==maxNum && over==maxNum){
obstacleGrid[i][j] = maxNum;
}else if(left==maxNum || over==maxNum){
obstacleGrid[i][j] = min(left, over);
}else{
obstacleGrid[i][j] = left + over;
}
}
}
if(obstacleGrid.back().back()==maxNum)
return 0;
else
return obstacleGrid.back().back();
}
};
这样做相当于是如果在过程中遇到了障碍物, 就把这个障碍物的信息继续往后传递, 一直到遍历结束.
这样当然可以解决问题, 并且整个遍历的过程也非常符合手工推导的直觉.
但是落实到代码层面的话, 不管是初始化的过程, 推导的过程, 还是最后得出结果的步骤, 都会变得更加繁琐, 不够简洁.
被障碍物阻挡后直接清空计数法(更简洁)
另一种思路: 将obstacleGrid
试做参考, 自己新建一个map
;
在遍历过程中如果当前位置有障碍物, 那么就直接给当前位置赋值0(清空前面的累计计数);
其含义也可以理解为: 有0种方法可以走到当前位置.
在初始化时, 遇到障碍物, 直接停止初始化.
class Solution {
private:
int maxNum = INT_MAX;
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)//一些trick, 起点终点处有障碍物就没法走了
return 0;
vector<vector<int>> map(m, vector<int>(n));
for(int i=0; i<n; i++){
if(obstacleGrid[0][i]==1)
break;
map[0][i] = 1;
}
for(int i=0; i<m; i++){
if(obstacleGrid[i][0]==1)
break;
map[i][0] = 1;
}
for(int i=1; i<m; i++){
for(int j=1; j<n; j++){
if(obstacleGrid[i][j]==1)
continue;
map[i][j] = map[i-1][j] + map[i][j-1];
}
}
return map[m-1][n-1];
}
};
总结
动态规划做起来真的比贪心舒服很多很多, 有逻辑的通畅感觉.
今天第二道题是第一道题的延伸拓展, 我虽然也做出来了, 但是用程序强行实现的手工推导思路, 并没有贴合dp数组的定义与实质.
导致算法不够简洁有力.
或许以后随着练习, 可以逐渐加强.
本文参考:
不同路径
不同路径 II