【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法

【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法

作者: @小小Programmer
这是我的主页:@小小Programmer
在食用这篇博客之前,博主在这里介绍一下其它高质量的编程学习栏目:
数据结构专栏:数据结构 这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏:算法 这里可以说是博主的刷题历程,里面总结了一些经典的力扣上的题目,和算法实现的总结,对考试和竞赛都是很有帮助的!
力扣刷题专栏:Leetcode想要冲击ACM、蓝桥杯或者大学生程序设计竞赛的伙伴,这里面都是博主的刷题记录,希望对你们有帮助!

【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法_第1张图片

先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力。看完之后别忘记关注我哦!️️️

本篇建议收藏后食用~

  • OJ练习1:62. 不同路径
    OJ练习2:63. 不同路径II

文章目录

    • 动态规划五部曲
    • OJ.62 不同路径
      • 题目描述
      • 动态规划解法
      • 深度优先搜索dfs解法
      • 两种方法效率对比
    • OJ.63 不同路径ll
      • 题目描述
      • 动态规划解法
    • 尾声

动态规划五部曲

  • 确定dp数组的含义—也就是dp[i][j]或者dp[i]到底是指的是啥,自己心里要很清楚。
  • 确定递推公式
  • 初始化dp数组–如果我们要递推,一开始的dp值我们肯定要知道的啦
  • 确定遍历顺序
  • 举例推导dp数组

OJ.62 不同路径

题目描述

【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法_第2张图片

动态规划解法

我们一步一步按照方法来解决这道题目:
因为我们每走到一个地方,我们只有两种选择,向右或者向下
所以我们可以建立二位的dp数组,来存放每个点位的信息

  1. dp[i][j] 存的是从(0,0)出发,到(i,j)的不同路径总数
  2. dp[i][j]很明显->dp[i][j]=dp[i-1][j]+dp[i][j-1]
  3. 初始化:从(0,0)->(i,0)或从(0,0)->(0,i)都是只有一条
  4. 我们每个点位的dp[i][j]都依赖于前面或者上面的数组值,所以我们肯定是从前向后遍历
  5. 我们可以尝试自己推导一下,看看能不能推出题目给的例子的答案,如果没问题,我们就基本可以确定,我们的思路没问题了。

完整代码:

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);
    }
};
//这样深度优先搜索,其实非常费时间,因为它的本质其实是递归

两种方法效率对比

我们先来看效率比较:
动态规划法:【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法_第3张图片
dfs:【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法_第4张图片
其实我们可以发现,dfs其实是跑不过OJ的,因为当行和列比较大的时候,递归这种方法其实效率是非常低的,而在这道题里面,动态规划其实是迭代的方法,所以效率更高!

OJ.63 不同路径ll

题目描述

【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法_第5张图片

动态规划解法

其实这一题和上一题的区别就是多了个障碍物而已,只要我们再初始化的时候,遇到障碍物,就跳出循环即可。

初始化步骤:
【算法】【动态规划】动规dp解决不同路径两道经典OJ笔试题【力扣62-力扣63】超详细的动态规划入门详解,掌握动态规划的解题方法_第6张图片
遍历的的时候:

  • 如果遍历到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、蓝桥杯或者大学生程序设计竞赛的伙伴,这里面都是博主的刷题记录,希望对你们有帮助!

你可能感兴趣的:(算法,跟着博主刷Leetcode,算法,c++,动态规划)