第九章动态规划——不同路径(一)

目录

力扣题号:62. 不同路径 - 力扣(LeetCode)

题目描述

示例

提示:

思路

解法一:动态规划

(1)dp数组的下标及其含义

(2)确定递推公式

(3)初始化递推数组

(4)确定遍历顺序

(5)根据题意推出dp数组对照

代码实现

解法二:**动态规划优化方法**

        递推公式

        初始化

 

解法三:数学

总结


力扣题号:62. 不同路径 - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

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

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

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

示例

示例 1:

输入: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


思路

解法一:动态规划

        其实这种路径问题,应该是可以使用DFS或者BFS吧,文章写到这里是我已经写完了动态规划的代码,准备写好思路之后突然灵光乍现,等我吧这里动态规划的思路写完我就去研究一下DFS或者BFS。

        好吧我直接回到这里,这里如果使用DFS转化成二叉树来解决,这个时间复杂度拉满了。我直接放弃了,毕竟这种题目还是适合使用动态规划。

        先确定一个思路:直行到达一个点,只有一个路径

        这里动态规划的思路我们还是根据五步骤来讲解:

(1)dp数组的下标及其含义

        这里的dp[i][j] 都不用想肯定是到达当前位置的路径数量了。

(2)确定递推公式

        在确定递推公式之前我们先观察一下问题,你会发现,第一行或者第一列的每一个位置都只有一种路径到达,因此你可以选择先把第一行和第一列全部赋值为 1,那么递推公式如下:

dp[i][j] = dp[i-1][j] + dp[i][j-1]

也就是到达当前位置只需要左边的点和右边的点的路径的和。

(3)初始化递推数组

        在我们这里的话,初始化的内容还有一点多,就是将第一行和第一列都全部初始化为1.

(4)确定遍历顺序

        思路都如此明确了,那么这个遍历顺序肯定是,从左到右从上到下了。

(5)根据题意推出dp数组对照

        

代码实现

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 i = 0; i < n; i++) {
            dp[0][i] = 1;
        }
        // 定义y方向的遍历
        for (int i = 1; i < m; i++) {
            // 定义x方向上的遍历
            for (int j = 1; j < n; j++) {
                // 这里i,j的点只需要加上左边和上边的路径数
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];

            }
        }
        for (int i = 0; i < m; i++) {
            System.out.println(Arrays.toString(dp[i]));
        }
        return dp[m - 1][n - 1];
    }
}

这咋还写出来了还这么慢。

wdf!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

但是不影响,我有应对之策

解法二:**动态规划优化方法**

        这里花费时间最多的在哪里,双层for循环对吧,但是这个核心代码基本优化不了。。。

下一位,我们初始化还有连个for循环,wdf??那第一行要是那么长,我就得初始化那么长,不行,这事我干不了。我不可能如此优化搞鸡毛啊。

        我们来想想,这里初始化就是将第一行和第一列进行初始化,也就是进行一个加一操作对吧,那我不初始化了,我在我的dp核心代码(双层for)里面进行+1的操作不可以吗,反正这段代码又优化不了,多加一个1也不影响。

        但是这里还是要注意,我们在dp核心里面多加一还不够,因为这只是相当于将一行一列的其中一个进行的初始化的操作,我们需要在最后的结果返回中再+1,这时候就有问题了。为什么不是在dp里面直接+2,那这样就多加了,因为过程是累加的嘛。

        递推公式

        所以这里的递推公式是有变化的如下所示:

dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + 1

        初始化

        这里初始化的话,你可以走个形式如下:

 dp[1][1] = 0

        有用吗,毫无卵用 ,我们都优化掉了初始化的过程,还初始化干嘛对吧。

        具体代码如下(注释及其详细):

class Solution {
    public int uniquePaths(int m, int n) {
        // 这里的m是网格的高度,n是网格的宽度
        // 当前位置的路径数  上面的路径数  左边的路径数
        // dp[i][j] = 1 + dp[i-1][j] + dp[i][j-1]
        //                   y        x
        int[][] dp = new int[m + 1][n + 1];
        // 初始化出发点为0路径
        dp[1][1] = 0;
        // 定义y方向的遍历
        for(int i = 1; i <= m; i++){
            // 定义x方向上的遍历
            for(int j = 1;j <= n;j++){
                // 你可以发现第一行也就是dp[1][n]的每个点都只有一种路径
                // 同理,第一列也是
                // 那么我们就排除掉第一行和第一列,最后加上即可
                if(i > 1 && j > 1 ){
                    // 这里i,j的点只需要加上左边和上边的路径数
                    // 为什么要+1,因为我们没有计算第一行或者第一列的路径
                    // 这里当然也可以不加1,那么你就需要将第一行和第一列都赋值为1
                    // 这样就不需要在这里加一了,当然这里加一还不够
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + 1;
                }
            }
        }
        // 由于将第一行和第一列设置成了0路径,我们在过程中只加了行或者列其中一个的1
        // 所以在最后的结果中需要加上另一个的1就是我们最终的答案了
        // 这样写可能不便于理解,但是少去了赋值的过程
        // 在较大的数据量的情况下应该是更快的
        return dp[m][n] + 1;
    }
}

那这还说啥了,easy!!!


解法三:数学

        看了一下力扣的题解,发现还可以使用数学方法,然后我细想了一下确实是可以使用排列组合来解决这个问题的。

        具体思路就是,一个矩形从左上角走到右下角,无论是哪一条路径,都需要 m + n - 2 步才可以到达。其中向下走 m-1 步, 向右走  n - 1 步, 那么就可以看做一个排列组合中的问题了(当然这里只有组合),我们需要在 m + n - 2 中选择 向下走 m-1 步的方案或者 向右走  n - 1 步的方案,其实答案都是一样的。

        所以我们的公式大概就是如下的样子:

C_{m + n - 2}^{n - 1}   或者是   C_{m + n - 2}^{m - 1}

class Solution {
    public int uniquePaths(int m, int n) {
		long res = 1;
		
		for (int x = n, y = 1; y <= m - 1; x++, y++) {
			res = res * x / y;
		}
		return (int) res;
	}
}

那这也没超过我的优化版动态规划的速度和内存占用啊,就这

总结

        这里就涉及到了二维的动态规划了,但是这里仍然不算是太难,对于初学者来说,并且是看完了我动态规划前两篇文章并且理解的同学来说,这道题应该是可以直接写出来的,或许想不到优化的方法,但是常规的方法应该还是没有问题的。

        总之,多学多练,勤能补拙加油呀ヾ(◍°∇°◍)ノ゙

  • 29
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WenJGo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值