leetcode:62. 不同路径

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述

题目解析

从暴力搜索到动态规划

暴力搜索

class Solution {
    // 机器人从[i, j]走到[m, n],一共有几种方法
    int process(int i, int j, int m, int n){
        if(i == m && j == n){
            return 1;  // 已经到了目的地
        }else if(i == m){  // 此时只能往右
            return process(i, j + 1, m, n);
        }else if(j == n){  // 此时只能往
            return process(i + 1, j, m, n);
        }else{
            return process(i, j + 1, m, n) + process(i + 1, j, m, n);
        }
    }
public:
    int uniquePaths(int m, int n) {
        return process(1, 1, m, n);
    }
};

暴力搜索改动态规划

(1)准备表,分析递归函数的可变参数个数与变化范围

 int process(int i, int j, int m, int n)
  • i,变化范围为[1, m]
  • j,变化范围为[1, n]

有两个参数就准备一个二维数组,如下:

 std::vector<std::vector<int>> dp(m + 1, std::vector<int>(n + 1, 0));

(2)返回值。分析主函数是怎么调用递归函数的

        return process(1, 1, m, n);

所以应该返回dp[1][1]

(3)填表

  • 先分析base case
	 if(i == m && j == n){
            return 1;  // 已经到了目的地
        }
  • 所以初始化dp[m][n] = 1
  • 在分析普通情况
  • 可以看出dp[i][j],依赖dp[i][j+1]、dp[i+1][j]、dp[i+1][j+1],所以应该从下到上,从右往左填
		else if(i == m){  // 此时只能往右
            return process(i, j + 1, m, n);
        }else if(j == n){  // 此时只能往
            return process(i + 1, j, m, n);
        }else{
            return process(i, j + 1, m, n) + process(i + 1, j, m, n);
        }

(4)综上,代码如下:

class Solution {
public:
    int uniquePaths(int m, int n) {
        if(m == 1 && n == 1){
            return 1;
        }

        std::vector<std::vector<int>> dp(m + 1, std::vector<int>(n + 1, 0));
        dp[m][n] = 1;
        for (int j = n - 1; j >= 1; --j) {
            dp[m][j] = dp[m][j + 1];
        }
        for (int i = m - 1; i >= 1; --i) {
            dp[i][n] = dp[i + 1][n];
        }

        for (int i = m - 1; i >= 1; --i) {
            for (int j = n - 1; j >= 1; --j) {
                dp[i][j] = dp[i][j + 1] + dp[i + 1][j];
            }
        }
        return dp[1][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);
    }
};

来分析一下时间复杂度,这个深搜的算法,其实就是要遍历整个二叉树。

这棵树的深度其实就是m+n-1(深度按从1开始计算)。

那二叉树的节点个数就是 2^(m + n - 1) - 1。可以理解深搜的算法就是遍历了整个满二叉树(其实没有遍历整个满二叉树,只是近似而已)

所以上面深搜代码的时间复杂度为O(2^(m + n - 1) - 1),可以看出,这是指数级别的时间复杂度,是非常大的。

排列组合

在这里插入图片描述

  • C ( m + n − 2 , m i n ( m − 1 , n − 1 ) ) C(m+n-2,min(m-1,n-1)) C(m+n2min(m1,n1));
    在这里插入图片描述
    在这里插入图片描述
class Solution {
	// 调用的时候,请保证初次调用时,m和n都不为0
    long gcd(long m, long n) {
        return n == 0 ? m : gcd(n, m % n);
    }
public:
     int uniquePaths(int m, int n) {
        int right = n - 1;
        int all = m + n - 2;
        long o1 = 1;
        long o2 = 1;
        // o1乘进去的个数 一定等于 o2乘进去的个数
        for (int i = right + 1, j = 1; i <= all; i++, j++) {
            o1 *= i;
            o2 *= j;
            long g = gcd(o1, o2);
            o1 /= g;
            o2 /= g;
        }
        return (int) o1;
    }
};

类似题目

题目思路
leetcode:62. 走到右下角有多少条路径 Unique Paths 机器人从[i, j]走到[m, n],一共有几种方法:先判断是不是到了目的地,如果是,那么找到了一种方法;判断是不是只能往右走;判断是不是只能往下走;否则往下+往右(之所以不检查边界是因为已经确保不超过边界))
leetcode:63. 走到右下角有多少条不同路径(可能有障碍) Unique Paths II机器人从[i, j]走到[m, n],一共有几种方法:先边界/障碍物检查,如果是,返回0;再终点检查,如果是,返回1;否则往下+往右
leetcode:64. 走到右下角最小路径和 Minimum Path Sum从终点往前面看,定义从左上角到坐标(i, j)的最短路径和:如果当前是起点,那么直接返回grid[0][0];否则判断是不是第一行,第一行只能从左边走过来 + grid[i][j];再判断是不是第一列,第一列只能从上面走下来 + grid[i][j];普通情况是:取从上面走下来和从左边走过来的最小值+当前坐标的值grid[i][j]
leetcode:174. 骑士救公主需要的最小血量 Dungeon Game逆序动态规划: 令dp[i][j]表示从坐标[i,j]到达终点所需要的最小初始血量。
leetcode:120. 三角形最小路径和
leetcode:980. 从起点到终点一共有多少条路径(有障碍,四个方向可以走,只能走一次,所有的可用格子要用完) III因为四个方向都可以走,所以必须回溯
leetcode:741. 最多能摘多少樱桃樱桃 Cherry Pickup 一个人来回走等价成两个人从起点走到终点。也就是两个人A、B都从左下角走到右下角,都只能向下或者向右走,但是A和B能做出不同的选择。如果,某一时刻,AB进入相同的格子,A和B就只能获得一份。如果某一个位置,A也来过,B也来过,AB一定是同时来的,而不会分先后,因为AB同时走
1463. 摘樱桃 II
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值