当前位置: 首页 > article >正文

【Leetcode 热题 100】62. 不同路径

问题背景

一个机器人位于一个 m × n m \times n m×n 网格的左上角。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。
问总共有多少条不同的路径?

数据约束

  • 1 ≤ m , n ≤ 100 1 \le m, n \le 100 1m,n100
  • 题目数据保证答案小于等于 2 × 1 0 9 2 \times 10 ^ 9 2×109

解题过程

这是个显而易见的组合问题,对于每个用例,总共要移动的步骤数量是固定的 ( m + n − 2 ) (m + n - 2) (m+n2),其中 ( m − 1 ) (m - 1) (m1) 步要从上往下走, ( n − 1 ) (n - 1) (n1) 步要从左往右走。
这样一来问题就转化成,有两种不同的选项,在 ( m + n − 2 ) (m + n - 2) (m+n2) 个位置上分别填入 ( m − 1 ) (m - 1) (m1) 个选项一和 ( n − 1 ) (n - 1) (n1) 个选项二,问一共有多少种不同的结果。
答案是 C m + n − 2 m − 1 C _ {m + n - 2} ^ {m - 1} Cm+n2m1 C m + n − 2 n − 1 C _ {m + n - 2} ^ {n - 1} Cm+n2n1,两者是等价的,该符号表示组合数的计算。
实现组合数,可以用两个索引来模拟数字增长的过程,抽象为累乘即可。
这样直接调用数学公式,时间复杂度是 O ( m ) O(m) O(m) 这个量级的。

此外,还可以用动态规划的思路去理解。
对于每一个位置,它是由上一个位置走一步过来的,这一步可能是从上往下走或者从左往右走。
所以到达这个位置的可能性总数,就是到达这两个前置位置的可能性总数之和。
由于每次计算只涉及到前一个状态,可以干掉一个维度。
涉及到二维状态转移,需要 O ( m n ) O(mn) O(mn) 这个水平的时间。

值得一提的是,这题交换行列并不影响结果。通过把相对较小的那个维度顾定成 m m m,可以进一步降低两种方法的理论时间或空间复杂度。
实际上并没有数量级上的提升,但是调用自身的写法很有意思,有必要积累一下。

具体实现

组合公式

class Solution {
    public int uniquePaths(int m, int n) {
        // 通过调用自身来固定 m
        if (m > n) {
            return uniquePaths(n, m);
        }
        long res = 1;
        for (int i = n, j = 1; j < m; i++, j++) {
            res = res * i / j;
        }
        return (int) res;
    }
}

动态规划

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 j = 0; j < n; j++) {
            dp[0][j] = 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];
    }
}

空间优化

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];
    }
}

http://www.kler.cn/a/527188.html

相关文章:

  • C#,入门教程(13)——字符(char)及字符串(string)的基础知识
  • AI编程:如何编写提示词
  • 代码随想录算法训练营第三十八天-动态规划-完全背包-139.单词拆分
  • Java中的注解与反射:深入理解getAnnotation(Class<T> annotationClass)方法
  • 【Rust自学】15.1. 使用Box<T>智能指针来指向堆内存上的数据
  • vscode+WSL2(ubuntu22.04)+pytorch+conda+cuda+cudnn安装系列
  • “LoRA技术中参数初始化策略:为何A参数采用正态分布而B参数初始化为0”
  • 解锁维特比算法:探寻复杂系统的最优解密码
  • 青少年编程与数学 02-008 Pyhon语言编程基础 04课题、开始编程
  • 【图床配置】PicGO+Gitee方案
  • 17.2 图形绘制3
  • Spring Web MVC基础第一篇
  • qsort应用
  • Manticore Search,新一代搜索引擎之王
  • 算法【分组背包】
  • 鸿蒙开发在onPageShow中数据加载不完整的问题分析与解决
  • 线段树(Segment Tree)和树状数组
  • FFmpeg(7.1版本)在Ubuntu18.04上的编译
  • 【二叉搜索树】
  • 2025-1-28-sklearn学习(47) (48) 万家灯火亮年至,一声烟花开新来。
  • Linux网络编程中的零拷贝:提升性能的秘密武器
  • 【PLL】参考杂散计算example
  • C++ 中的类(class)和对象(object)
  • P11467 网瘾竞赛篇之 generals 大法好
  • Java中的线程池参数(详解)
  • Python 学习进阶技术文档