动态规划基础部分05不同路径问题

129 阅读4分钟

不同路径

力扣62. 不同路径 - 力扣(LeetCode)
一个机器人位于一个 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
这道题⽬,开始⼀看最直观的想法就是⽤图论⾥的递归深搜,来枚举出来有多少种路径。注意题⽬中说机器⼈每次只能向下或者向右移动⼀步,那么其实机器⼈⾛过的路径可以抽象为⼀颗⼆叉树,⽽叶⼦节点就是终点!
如图举例:
image.png
此时问题就可以转化为求⼆叉树叶⼦节点的个数,Java代码如下:

class Solution {
    public int uniquePaths(int m, int n) {
        return dfs(0, 0, m, n);
    }  
    
    public int dfs(int i, int j, int m, int n){
        if(i >= m || j >= n) // 数组越界
            return 0;
        if(i == m - 1 && j == n - 1)//找到了!返回
            return 1;
        return dfs(i + 1, j, m, n) + dfs(i, j + 1, m, n);
    }
}

⼤家如果提交了代码就会发现超时了!来分析⼀下时间复杂度,这个深搜的算法,其实就是要遍历整个⼆叉树。这颗树的深度其实就是m+n-1(深度按从1开始计算)。那⼆叉树的节点个数就是 2^(m + n - 1) - 1。可以理解深搜的算法就是遍历了整个满⼆叉树(其实没有遍历整个满⼆叉树,只是近似⽽已)所以上⾯深搜代码的时间复杂度为O(2^(m + n - 1) - 1),可以看出,这是指数级别的时间复杂度,是⾮常⼤的。接着我们来用动态规划来解题。

动规五部曲:

1. 确定dp数组以及下标的含义

因为机器⼈从(0 , 0) 位置触发,到(m - 1, n - 1)终点。所以定义二维数组dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。

2. 确定递推公式

想要求dp[i][j],只能有两个⽅向来推导出来,即dp[i - 1][j] 和 dp[i][j - 1]。此时在回顾⼀下 dp[i - 1][j] 表示啥,是从(0, 0)的位置到(i - 1, j)有⼏条路径,dp[i][j - 1]同理。那么很⾃然,dp[i][j] = dp[i - 1][j] + dp[i][j - 1],因为dp[i][j]只有这两个⽅向过来。

3. dp数组如何初始化

如何初始化呢,⾸先dp[i][0]⼀定都是1,因为从(0, 0)的位置到(i, 0)的路径只有⼀条,那么dp[0][j]也同 理。

image.png

所以初始化代码为:     
for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int j = 0; j < n; j++) dp[0][j] = 1;    

4. 确定遍历顺序

这⾥要看⼀下递归公式dp[i][j] = dp[i - 1][j] + dp[i][j - 1],dp[i][j]都是从其上⽅和左⽅推导⽽来,那么从左到右⼀层⼀层遍历就可以了。这样就可以保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][j - 1]⼀定是有数值的。当然从上往下再从左往右也是一样的。

5. 举例推导dp数组

如图所示:
image.png
Java代码如下:

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        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++){
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
    return dp[m - 1][n - 1];
    }  
}

其实⽤⼀个⼀维数组(也可以理解是滚动数组)就可以了,但是不利于理解,可以优化点空间,建议先理解了⼆维,在理解⼀维,步骤如图:

image.png

Java代码如下:

class Solution {
    public int uniquePaths(int m, int n) {
        int[] dp = new int[n];
        for(int i = 0; i < n; i++)
            dp[i] = 1;
        for(int i = 1; i < m; i++)
            for(int j = 1; j < n; j++){
                    dp[j] += dp[j - 1] ;
            }
    return dp[n - 1];
    }  
}

注:文章参考微信公共号:代码随想录