LeetCode刷题记录----62.不同路径(Medium)

2025/9/26

题目(Medium):

62.不同路径(Medium)


我的思路:

模式识别: 要走到m-1,n-1就需要先走到m-2,n-1或者m-1,n-2的位置,而走到上述两个位置也需要同样往前推导,因此主问题和子问题有相同的结构和状态含义 --> 动态规划

因为是动态规划,所以我们要考虑各个状态以及状态的转移方程

显然需要对每个格子记录状态,所以我们用二维数组:

dpi, j 到达i, j位置的不同路径数

状态转移方程: dpi, j = dpi-1, j + dpi, j-1 【因为每次从左边或者上边的格子到达i,j一定只有一种方法】

初始状态: 现在到达第一行或者第一列的格子都一定只有一条路径,所以都初始化为1

具体代码如下:

cs 复制代码
public class Solution {
    public int UniquePaths(int m, int n) {
        //第一行/第一列的格子显然都只有一种方式到达
        int[,] dp = new int[m, n];
        for(int j = 0; j < n; j++)
            dp[0, j] = 1;
        for(int i = 0; i < m; i++)
            dp[i, 0] = 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];
    }
}

时间复杂度:O(MN)

空间复杂度:O(MN)


优化思路:

1.压缩为一维动态规划

在上面的转移方程:dpi, j = dpi-1, j + dpi, j-1 中,我们可以看出来当前第 i 行,第j列的dp值只和它这一行的j-1列,以及上一行的j列元素有关。

因此我们可以考虑把他压缩为一维的,这样每次在计算新dpj的时候,都是计算最新一行的dpj值,同时对于还没计算的位置相当于保留了上一行的dpj的值。

新转移方程:dpj = dpj + dj-1

初始化:全部为1,因为第一行全部为1

翻译:当前行的dpj = 上一行的dpj + 当前行的dpj-1

具体代码如下:

cs 复制代码
public class Solution {
    public int UniquePaths(int m, int n) {
        //第一行/第一列的格子显然都只有一种方式到达
        int[] dp = new int[n];
        Array.Fill(dp, 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];
                dp[j] += dp[j-1];       //可以压缩为一维的,因为可以看到每一行的状态只与这一行以及她的上一行有关
            }
        }

        return dp[n-1];
    }
}

时间复杂度:O(MN)

空间复杂度:O(N)

2.组合数

我们还可以通过数学原理 ,要到达m行,n列的位置,一共需要移动的次数为m+n-2次 。其中需要向下移动的次数是m-1次,而题目要求的相当于就是在这m+n -2次中,选取m-1种不同的下移方案。

因此可以用该组合数公式来进行计算(公式图片来自于LeetCode官方题解)

其中我们通过这里来下手,进行累乘计算(下面展示部分累乘过程)

n/1 ,(n /1) * (n+1/2),(n/1) * (n+1/2) * (n+2/3) ...以此类推到最终答案值

因此我们可以用变量 x表示范围n ~ n + m -2 的分子,用变量y 表示范围1 ~ m-1的分母

具体代码如下:

cs 复制代码
public class Solution {
    public int UniquePaths(int m, int n) {
        //还可以计算组合数
        //一共要移动m+n-2次,其中存在m-1种往下移动的次数
        long ans = 1;
        long x, y; 
        for(x = n, y = 1; y < m; x++, y++){
            ans = ans * x/ y;       //存在一个推导公式
        }
        
        return (int)ans;
    }
}

时间复杂度:O(M)

空间复杂度:O(1)

注意:这里累乘的计算过程种有可能会出现超过int的上限的数字,所以用long来存储

这个来自LeetCode的一个评论区的补充解释,忘记了可以回来看一眼


总结:

①对于路径数总和问题,如果存在确定的移动规则,那我们可以很简单地通过动态规划来解决。

②当状态转移方程种发现当前这一行的状态可以仅由当前行的状态和上一行的状态得到的时候,可以考虑压缩为一维动态规划

③你永远可以相信数学原理的力量

相关推荐
倒头就睡的小比特21 小时前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!21 小时前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼1 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光1 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark1 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her11 天前
并查集-听课笔记
笔记·算法·并查集
码流子1 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven1 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark1 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218611 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法