美文网首页
0062. 不同路径

0062. 不同路径

作者: 蓝笔头 | 来源:发表于2021-08-28 21:42 被阅读0次

题目地址

https://leetcode-cn.com/problems/unique-paths/

题目描述

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

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

问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?



示例 1:

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

输入: m = 7, n = 3
输出: 28


提示:

1 <= m, n <= 100
题目数据保证答案小于等于 2 * 10 ^ 9

题解

暴力枚举

通过回溯算法枚举所有的状态,到达终点路径加 1

class Solution {
    private int pathNum = 0;

    public int uniquePaths(int m, int n) {
        pathNum = 0;

        dfs(m, n, 1, 1);

        return pathNum;
    }

    public void dfs(int m, int n, int i, int j) {
        // 超出范围
        if (i > m || j > n) return;

        // 到达终点
        if (i == m && j == n) {
            pathNum++;
            return;
        }

        // case 1,向右移动一步
        dfs(m, n, i+1, j);

        // case 2,向下移动一步
        dfs(m, n, i, j+1);
    }
}

超出时间限制,说明有优化方案。

组合数

机器人只能【向下】或【向右】移动一步。

对于一个 m * n 的网格,从左上角移动到右下角,向下要走 m - 1 步,向右要走 n - 1 步。一共走了 (m - 1) + (n - 1) = m + n - 2 步。

这个问题可以看成,从 m + n - 2 个移动方向里面选出 m - 1 个向下的方向,剩余的就是向右。

因此是一个组合数计算的问题。

组合数公式是指从 n 个不同元素中,任取 m(m <= n) 个元素并成一组,叫做从 n 个不同元素中取出 m 个元素的一个组合;
n 个不同元素中取出 m(m <= n) 个元素的所有组合的个数,叫做 n 个不同元素中取出 m 个元素的组合数。用符号 C(n,m) 表示。

公式写法 C(m,n) = A(m,n) / n!

int 类型溢出

代码如下所示:

class Solution {
    private int pathNum = 0;

    public int uniquePaths(int m, int n) {
        // 可以向下走 (m-1) 步
        // 可以向右走 (n-1) 步
        int cN = (m-1) + (n-1);
        int cM = (m-1);
        return C(cN, cM);
    }

    /**
        组合数公式:C(n,m)=n!/((n-m)!*m!)(m≤n)
     */
    public int C(int n, int m) {
        return factorial(n) / (factorial(n - m) * factorial(m));
    }

    public int factorial(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }

        return factorial(n-1) * n;
    }
}
int 类型溢出了

long 类型溢出

    /**
        组合数公式:C(n,m)=n!/((n-m)!*m!)(m≤n)
     */
    public int C(int n, int m) {
        long result = factorial(n) / (factorial(n - m) * factorial(m));
        return (int)result;
    }

    public long factorial(int n) {
        if (n == 0 || n == 1) {
            return 1;
        }

        return factorial(n-1) * n;
    }
long 类型也溢出了

BigInteger 存储数值

// 注意需要导出 BigInteger 类
import java.math.*;

class Solution {
    private int pathNum = 0;

    public int uniquePaths(int m, int n) {
        // 可以向下走 (m-1) 步
        // 可以向右走 (n-1) 步
        int cN = (m-1) + (n-1);
        int cM = (m-1);
        return C(cN, cM);
    }

    /**
        组合数公式:C(n,m)=n!/((n-m)!*m!)(m≤n)
     */
    public int C(int n, int m) {
        BigInteger result = factorial(n).divide(factorial(n - m).multiply(factorial(m)));
        return result.intValue();
    }

    public BigInteger factorial(int n) {
        if (n == 0 || n == 1) {
            return BigInteger.ONE;
        }

        return factorial(n-1).multiply(BigInteger.valueOf(n));
    }
}

动态计算组合数 C(m, n) 的值

    /**
        组合数公式:C(n,m)=n!/((n-m)!*m!)(m≤n)
     */
    public int C(int n, int m) {
        int begin = (n-m+1);
        long result = 1;
        for (int i = 1; i <= m; ++ i) {
            result *= begin;
            result /= i;

            begin++;
        }

        return (int)result;
    }

动态规划

定义 dp[i][j] 为当前位置走到右下角的路径数。

因此状态转移方程可以写作:dp[i][j] = dp[i + 1][j] + dp[i][j + 1]

  • dp[i + 1][j] 表示向下移动一步。
  • dp[i][j + 1] 表示向右移动一步。

对于一个 m * n 的网格,base case 为:

  • 最右边一列只有一条路径,即 dp[i][n - 1] = 10 <= i < m
  • 最下面一行只有一条路径,即 dp[m - 1][j] = 10 <= j < n
class Solution {
    private int pathNum = 0;

    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        // 最右边的一列初始化为 1
        for (int i = 0; i < m; ++ i) {
            dp[i][n - 1] = 1;
        }
        // 最下面的一行初始化为 1
        for (int j = 0; j < n; ++ j) {
            dp[m - 1][j] = 1;
        }

        // 从未赋值的右下角位置开始遍历
        for (int i = m - 2; i >= 0; i --) {
            for (int j = n - 2; j >= 0; j --) {
                dp[i][j] = dp[i+1][j] + dp[i][j+1];
            }
        }

        return dp[0][0];
    }

}

参考

相关文章

  • 0062. 不同路径

    题目地址 https://leetcode-cn.com/problems/unique-paths/[https...

  • 不同路径

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

  • 不同的路径

    LeetCode题目链接有一个机器人的位于一个 m × n 个网格左上角。机器人每一时刻只能向下或者向右移动一步。...

  • 不同路径

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

  • 不同路径

    题目来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/uniq...

  • 不同路径

    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。机器人每次只能向下或者向右...

  • 不同路径

    题目描述:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 机器人每次只能...

  • 不同路径

    题目描述: 一个机器人位于一个m x n网格的左上角 (起始点在下图中标记为“Start” )。 机器人每次只能向...

  • 关于URL路径

    ajax 发请求时,所带的路径可以是绝对路径也可以是相对路径,不同的路径浏览器会有不同的拼接方式 当请求路径为相对...

  • Python小白 Leetcode刷题历程 No.61-N

    Python小白 Leetcode刷题历程 No.61-No.65 旋转链表、不同路径、不同路径Ⅱ、最...

网友评论

      本文标题:0062. 不同路径

      本文链接:https://www.haomeiwen.com/subject/atisiltx.html