【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法
【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法
作者: @小小Programmer
这是我的主页:@小小Programmer
在食用这篇博客之前,博主在这里介绍一下其它高质量的编程学习栏目:
数据结构专栏:数据结构 这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏:算法 这里可以说是博主的刷题历程,里面总结了一些经典的力扣上的题目,和算法实现的总结,对考试和竞赛都是很有帮助的!
力扣刷题专栏:Leetcode想要冲击ACM、蓝桥杯或者大学生程序设计竞赛的伙伴,这里面都是博主的刷题记录,希望对你们有帮助!
先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力。看完之后别忘记关注我哦!️️️
本篇建议收藏后食用~
- OJ练习1:62. 不同路径
OJ练习2:63. 不同路径II
动态规划五部曲
- 确定dp数组的含义—也就是
dp[i][j]
或者dp[i]
到底是指的是啥,自己心里要很清楚。 - 确定递推公式
- 初始化
dp
数组–如果我们要递推,一开始的dp
值我们肯定要知道的啦 - 确定遍历顺序
- 举例推导
dp
数组
OJ.62 不同路径
题目描述
动态规划解法
我们一步一步按照方法来解决这道题目:
因为我们每走到一个地方,我们只有两种选择,向右或者向下
所以我们可以建立二位的dp数组,来存放每个点位的信息
dp[i][j]
存的是从(0,0)
出发,到(i,j)
的不同路径总数dp[i][j]
很明显->dp[i][j]=dp[i-1][j]+dp[i][j-1]
- 初始化:从
(0,0)->(i,0)
或从(0,0)->(0,i)
都是只有一条 - 我们每个点位的
dp[i][j]
都依赖于前面或者上面的数组值,所以我们肯定是从前向后遍历 - 我们可以尝试自己推导一下,看看能不能推出题目给的例子的答案,如果没问题,我们就基本可以确定,我们的思路没问题了。
完整代码:
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 i = 0; i < n; i++)dp[0][i] = 1;
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {//注意注意,这两个要从1开始遍历,因为0已经初始化好了,再0-1会越界
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
};
深度优先搜索dfs解法
当然,由于这题的特殊性,我们除了用动态规划来处理之外,我们还可以用深度优先搜索的思想,搜索路径。
- 由于我们每到一个点,都只能向右或者向下走,所以,我们完全可以把这个路径看成二叉树的树枝,这个问题其实就变成一个简单的,二叉树找叶子节点的问题。
//解法1:深度优先搜索->看成一棵二叉树
//因为只能向右或者向下走,所以可以看成二叉树
class Solution {
private:
int dfs(int i, int j, int m, int n) {
if (i > m || j > n)return 0;//越界了
if (i == m && j == n)return 1;//找到一条路
return dfs(i + 1, j, m, n) + dfs(i, j + 1, m, n);
}
public:
int uniquePaths(int m, int n) {
return dfs(1, 1, m, n);
}
};
//这样深度优先搜索,其实非常费时间,因为它的本质其实是递归
两种方法效率对比
我们先来看效率比较:
动态规划法:
dfs:
其实我们可以发现,dfs其实是跑不过OJ的,因为当行和列比较大的时候,递归
这种方法其实效率是非常低的,而在这道题里面,动态规划其实是迭代
的方法,所以效率更高!
OJ.63 不同路径ll
题目描述
动态规划解法
其实这一题和上一题的区别就是多了个障碍物而已,只要我们再初始化的时候,遇到障碍物,就跳出循环即可。
初始化步骤:
遍历的的时候:
- 如果遍历到
dp[i][j]
的时候,发现(i,j)
上有障碍物,说明这条路走不通了,直接跳过本次循环即可。
完整代码:
//OJ63 不同路径2
//这题与上一题的区别就是这题有障碍物
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
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 i = 0; i < n && obstacleGrid[0][i] == 0; i++)
//如果遇到障碍物,直接跳出循环
dp[0][i] = 1;
//开始遍历
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (obstacleGrid[i][j] == 1)continue;//如果遇到障碍物,直接continue跳过该层循环
//上面这个continue的意思,就是留着(i,j)这个位置不处理了,它就是0,不用管它
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
};
尾声
看到这里相信你对这两道OJ已经有一定的理解了,其实路径问题是一个非常经典的我们必须要掌握的问题。
在走之前,可以再看看博主的这些专栏内容对你是否有帮助:有的话请不要吝啬你们的点赞收藏关注和转发噢!
数据结构专栏:数据结构 这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏:算法 这里可以说是博主的刷题历程,里面总结了一些经典的力扣上的题目,和算法实现的总结,对考试和竞赛都是很有帮助的!
力扣刷题专栏:Leetcode想要冲击ACM、蓝桥杯或者大学生程序设计竞赛的伙伴,这里面都是博主的刷题记录,希望对你们有帮助!