LeetCode高频题62. 不同路径:机器人从左上角到右下角的路径有多少条?纯概率排列组合问题,而不是动态规划题

LeetCode高频题62. 不同路径:机器人从左上角到右下角的路径有多少条?纯概率排列组合问题,而不是动态规划题

提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
在这里插入图片描述


题目

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

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

问总共有多少条不同的路径?

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/unique-paths
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


一、审题

示例:
输入:m = 3, n = 7
输出:28
示例 2:

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

  1. 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右
  3. 向下 -> 向右 -> 向下
    示例 3:

输入:m = 7, n = 3
输出:28
示例 4:

输入:m = 3, n = 3
输出:6

提示:

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


二、解题

这个题目,你要是想着用动态规划解,问题也不大,能解出来
但是过于复杂了,填写一个o(n*m)的表格,太复杂了

其实这题跟上次我讲的斗地主很类似,纯属一个概率问题,一个排列组合问题

【1】复盘:一副牌(54张),三人斗地主,大小王在同一家的概率是多少

咱们来看看啊

实际上一个表格,你每一次,不是往右,就是往下,就2种走法
但是不论怎么走,你得从中选择一个走法

想从左上角到右下角走到目标位置
一共要走多少步呢??m-1+n-1也就是必须往下走m-1步,再往右走n-1步,这是你必须走的

那么实际上在这m+n-2步走法中,你必须往下走m-1步,其余时随意选,
因此,在什么时候选择走着m-1步,不就是一个排列组合问题吗???
所以走法就是C(m-1+n-1,m-1)

这就是一句代码的事情!!!

这是我之前一直迷糊的事情,就是算阶乘咋搞???????

问题是,你怎么计算C(n,k)呢???这可是谁阶乘的计算呢!

在这里插入图片描述
实际上代码求的过程中,其实n!与(n-k)!是以约掉到
在这里插入图片描述
即使如此,可能我们算阶乘的时候,阶乘仍然可能会溢出,超过int表示的范围

因此,我们中途要想办法,先把分子分母约一下!!!!

那就是说,要把分子分母的最大公约数gcd找出来,这样的话分子分母,先除gcd,这样就变小了

啥意思?
你看下面
在这里插入图片描述
我们在算C(9,3)时,首先控制分子a实际上是9-3+1=7开头的数到9相乘
分母b是1开头到3,相乘
然后a其实最开始=7,b=1,我们约为最简式子7/1
下一次
a=78,b=12
这时,想办法约掉ab的最大公约gcd=2,得到a=74,b=1
下一次
a=7
49,b=13
这时,想办法约掉ab的最大公约gcd=3,得到a=743,b=1
这样的话,其实ab不会太大
最终b是要约为1的,因此把a返回,就是C(9,3)的结果

这就是算C(n,k)的求法!!!

gcd(a,b)的公式,我们早讲过无数次,死记硬背:b=0,返回a,否辗转相处求gcd(b,a%b)

        //gcd(a,b)的公式,我们早讲过无数次,死记硬背:b=0,返回a,否辗转相处求gcd(b,a%b)
        public static int gcd(int a, int b){
            return b == 0 ? a : gcd(b, a % b);
        }

知道了吧:
测试一把:

    public static void test(){
        Solution solution = new Solution();
        System.out.println(solution.uniquePaths(3, 7));
    }

    public static void main(String[] args) {
//        test();
        test2();
    }

结果2
问题不大

正式用gcd来月C(n,k)的分子分母,防止溢出,计算C(n,k)

上面说了哦,分母a从n-k+1开始到n连成阶乘
分子b从1–k连成阶乘
a/b在循环时将gcd约了

这样防止ab溢出,懂吧?代码很简单

        //正式用gcd来月C(n,k)的分子分母,防止溢出,计算C(n,k)
        public int Cnk(int n, int k){
            int a = 1;//分母
            int b = 1;//分子
            //上面说了哦,分母a从n-k+1开始到n连成阶乘
            //分子b从1--k连成阶乘
            //a/b在循环时将gcd约了
            //i帮着分母循环,j帮着分子循环
            for (int i = n - k + 1, j = 1; i <= n || j <= k; i++, j++) {
                //当i或者j越界了也没事,因为ab,最终会约的
                a *= i;
                b *= j;//分母分子轮番乘自己的阶乘
                //然后约
                int gcd = gcd(a, b);
                a /= gcd;
                b /= gcd;
            }

            return a / b;
        }

测试:

System.out.println(solution.Cnk(3,1));

结果3
问题不大

OK,最终一行代码求解本题,究竟m*n矩阵,从左上角到右下角的路径有多少条???

一共走法n+m-2种,其中啥时候决定往下m-1步

一个组合问题,不是动态规划的复杂度思路
手撕代码:

        //究竟m*n矩阵,从左上角到右下角的路径有多少条???
        public int uniquePathsReview(int m, int n) {

            //就一个排列组合问题
            return Cnk(m + n - 2, m - 1);
        }

测试:

System.out.println(solution.uniquePathsReview(3,7));

结果28

来,我们LeetCode测试一把:
中途,上面那些代码,用int接受ab,实际上还是可能溢出的
最后我们把int改long接收ab,最后返回C(n,k)时,直接用int转化即可

class Solution {
        //压根不需要用动态规划求
        //直接排列组合
        //gcd(a,b)的公式,我们早讲过无数次,死记硬背:b=0,返回a,否辗转相处求gcd(b,a%b)
        public long gcd(long a, long b){
            return b == 0 ? a : gcd(b, a % b);
        }

        //正式用gcd来月C(n,k)的分子分母,防止溢出,计算C(n,k)
        public int Cnk(int n, int k){
            long a = 1;//分母
            long b = 1;//分子
            //上面说了哦,分母a从n-k+1开始到n连成阶乘
            //分子b从1--k连成阶乘
            //a/b在循环时将gcd约了
            //i帮着分母循环,j帮着分子循环
            for (int i = n - k + 1, j = 1; i <= n || j <= k; i++, j++) {
                //当i或者j越界了也没事,因为ab,最终会约的
                a *= i;
                b *= j;//分母分子轮番乘自己的阶乘
                //然后约
                long gcd = gcd(a, b);
                a /= gcd;
                b /= gcd;
            }

            return (int) (a / b);
        }

        //究竟m*n矩阵,从左上角到右下角的路径有多少条???
        public int uniquePaths(int m, int n) {

            //就一个排列组合问题
            return Cnk(m + n - 2, m - 1);
        }
}

老牛逼了:
在这里插入图片描述
在这里插入图片描述
那些动态规划的复杂解法,也不是不行
我之前还写过

动态规划写法,你看看就行

//暴力递归
        public int uniquePaths1(int m, int n) {
            //反过来思考,从m-1,n-1点开始走,啥时候走到00就行,然后只能往左走喝上走

            return process(m, n, m - 1, n - 1);//从右下角出发,到左上角有多少方案
        }

        //当前处在xy,去00处的方案数量
        public int process(int m, int n, int x, int y){
            if (x == 0 && y == 0) return 1;//算一种有效方案
            if (x < 0 || y < 0) return 0;//无效方案

            //还没走到的话
            int res = 0;
            res += process(m, n, x - 1, y);//往左
            res += process(m, n, x, y - 1);//往上
            return res;
        }

        //暴力递归是不行的,需要写成dp
        public int uniquePaths(int m, int n) {
            //反过来思考,从m-1,n-1点开始走,啥时候走到00就行,然后只能往左走喝上走
            int[][] dp = new int[m][n];
            dp[0][0] = 1;
            //开始填表,最后取末端那个
            for (int x = 0; x < m; x++) {
                for (int y = 0; y < n; y++) {
                    if (x == 0 && y == 0) continue;
                    dp[x][y] = x - 1 >= 0 ? dp[x - 1][y] : 0;
                    dp[x][y] += y - 1 >= 0 ? dp[x][y - 1] : 0;
                }
            }

            return dp[m - 1][n - 1];//从右下角出发,到左上角有多少方案
        }

就是说,从右下角,走到00处
能走到,算一种,否则向左,或者向上,不越界的话,可以考虑走走试试

动态规划也是可以的


总结

提示:重要经验:

1)本题,实际上最大的收货,在于我们如何求排列组合C(n,k),防止分子分母越界溢出int,那就要通过不断求ab最大公约数gcd来约,这样不会溢出,在单调栈那求C(k,2),当时问题规模很小,不需要这样复杂
2)另外,ab的最大公约数,死记硬背,就是gcd(a,b)=b==0?a:gcd(b,a%b);
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值