LeetCode-62-不同路径

大憨熊 提交于 2020-01-31 03:07:22

题意描述:

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?
在这里插入图片描述
例如,上图是一个7 x 3 的网格。有多少可能的路径?

说明:m 和 n 的值均不超过 100。


示例:

  • 示例一:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
  • 示例二:
输入: m = 7, n = 3
输出: 28

解题思路:

Alice: 这题我会呀,可以用高中学的排列组合的知识来解。每次只能往下往右走,那么往下走的总步数是确定的,就是 n-1步,同样往右走的总步数也是确定的,就是m-1步。只要我们安排好这 m-1 步 和 n-1 步的顺序就唯一确定了一种行动方案。
Bob: 然后呢,该怎么安排呢 ?
Alice: 我们可以把 n-1 步插入到 m-1 步的缝隙中去啊,然后去除重复的部分,因为 n-1 步各个部分的拓扑序无论如何排列都是相同的答案。
Bob:你是说这样吗 ?
在这里插入图片描述
Alice: 对对对,然后公式是,,额,我还是直接写伪代码吧。

ret = 1
for(i=0; i<n-1; ++i){
   ret *= m
   m += 1       // 因为每次添加一个元素后 可供添加的位置又增加了一个
}
ret / A(n-1)    // 最后除去 n-1 的全排列数就好了

Bob: 厉害呀,这样应该是O(1) 的时间复杂度吧,相当于找到通项公式了。
Alice: hhh,算全排列还有组合数还写了循环呢,再快也得是O(n)吧。
Bob: 😎😎。
Alice: 还有动态规划的解法呢
Bob: 这样吗 ?
在这里插入图片描述
Alice: 对,可以用一个二维数组来记录,dp[i][j] 就是到达 坐标为 i, j 方格的不同路径总数。
Bob: 还可以优化空间复杂度的。😎😎


代码:

Python 方法一: 排列组合算一算。时间复杂度 O(n),n 是输入的列数。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:

        xSteps = m - 1
        ySteps = n - 1
        ret = 1

        for x in range(ySteps):
            xSteps += 1
            ret *= xSteps
        for x in range(1, ySteps+1):
            ret //= x

        return ret 

Java 方法一: 还是排列组合,不过注意运算过程中整型溢出的问题。

class Solution {
    public int uniquePaths(int m, int n) {

        int xSteps = m - 1;
        int ySteps = n - 1;
        int[] flags  = new int[n];
        long ret    = 1; 
        // 使用长整型,提前开始除法,防止运算过程中溢出
        for(int i=0; i<ySteps; ++i){
            xSteps += 1;
            ret *= xSteps;
            if(ret % (i+1) == 0){
                ret /= i+1;
            }else{
                flags[i] = 1;
            }
        }

        for(int i=0; i<n; ++i){
            if(flags[i] == 1){
                ret /= i+1;
            }
        }

        return (int)ret;
    }
}

Python 方法二: 动态规划,dp[x][z] = dp[x-1][z] + dp[x][z-1]。也就是到达任何一个位置的方法个数等于到达该位置的左侧位置和上方位置的方法总数之和。 或者,到达任何位置的最后一步有两种走法,从上往下或者从左往右。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:

        dp = [[1 for x in range(m)] for z in range(n)]
        for x in range(1, n):
            dp[x][0] = 1

        for x in range(1, n):
            for z in range(1, m):
                dp[x][z] = dp[x-1][z] + dp[x][z-1]
        
        return dp[n-1][m-1]       

Python 方法二: 动态规划 + 优化空间复杂度,仅使用前后两行来记录。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:

        lastRow = [1 for x in range(m)]
        currRow = [1 for x in range(m)]

        for x in range(1, n):
            for z in range(1, m):
                currRow[z] = lastRow[z] + currRow[z-1]
            lastRow[:] = currRow[:]
            
        return currRow[-1]       

Python 方法二: 动态规划 + 继续优化空间复杂度。但是直接看代码并不好理解。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:

        currRow = [1 for x in range(m)]

        for x in range(1, n):
            for z in range(1, m):
                currRow[z] += currRow[z-1]
                
        return currRow[-1]     

Java 方法二: 动态规划 + 优化空间复杂度。

class Solution {
    public int uniquePaths(int m, int n) {

        // init
        int[] currRow = new int[m];
        for(int i=0; i<m; ++i){
            currRow[i] = 1;
        }

        //get answer
        for(int cnt=1; cnt<n; ++cnt){
            for(int j=1; j<m; ++j){
                currRow[j] += currRow[j-1];
            }
        }

        return currRow[m-1];
    }
}

易错点:

  • 一些测试用例:
1
1
1
2
2
1
3
2
7
3
13
13
17
17
  • 答案:
1
1
1
3
28
2704156
601080390

总结:

在这里插入图片描述


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!