leetcode
leetcode 51 ~ 100
不同路径

不同路径

难度:

标签:

题目描述

一个机器人位于一个 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 * 109

代码结果

运行时间: 44 ms, 内存: 15.3 MB


/*
 * 问题描述:
 * 给定一个 m x n 的网格,一个机器人位于左上角,只能向右或向下移动,目标是到达右下角。求不同路径的数量。
 * 题目思路:
 * 使用 Java Stream 可以简化部分代码。我们依然使用动态规划的思想,维护一个一维数组来存储路径数量。
 * 初始条件:初始化数组,起始点设为 1。
 * 状态转移:遍历网格时不断更新数组中的路径数。
 */
 
import java.util.stream.IntStream;
 
public class Solution {
    public int uniquePaths(int m, int n) {
        int[] dp = new int[n];
        dp[0] = 1; // 初始化起点
        for (int i = 0; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[j] += dp[j - 1];
            }
        }
        return dp[n - 1];
    }
}
 

解释

方法:

这个题解使用动态规划的思路来解决问题。定义函数 dp(i, j) 表示从起点走到位置 (i, j) 的不同路径数量。机器人每次只能向下或向右移动,因此到达 (i, j) 的路径数等于到达 (i-1, j) 和 (i, j-1) 的路径数之和。使用记忆化搜索避免重复计算子问题,将已计算过的结果存储在 memo 字典中。边界条件是当 i 或 j 小于0时路径数为0,当到达起点 (0, 0) 时路径数为1。最终返回 dp(m-1, n-1) 即为从起点到终点的不同路径总数。

时间复杂度:

O(m*n)

空间复杂度:

O(m*n)

代码细节讲解

🦆
在递归函数中,如果(i, j)位置超出了网格的边界,为什么返回0?这是否意味着机器人不能往回走?
在递归函数中,当(i, j)位置超出网格边界时返回0的原因是,这代表机器人不能进入这些位置,因此这些位置不存在有效的路径到达它们。此设定并不意味着机器人不能往回走,而是根据题目设定,机器人只能向右或向下移动,不允许向左或向上,因此超出边界的位置自然不在考虑范围内。
🦆
解释一下为什么当机器人到达起点(0, 0)时,路径数为1?是否存在其他情况起点路径数不为1的情况?
当机器人到达起点(0, 0)时,路径数为1是因为起点是路径的开始点,且只存在一种方式在起点上开始路径——即站在起点上。这是由题目定义决定的,因为所有的路径都从此点出发。在这个问题的设定中,不存在其他情况使得起点路径数不为1,因为起点总是固定的,且机器人总是从这一点开始移动。
🦆
如果输入的网格尺寸非常大,比如m和n都很大,有什么方式可以优化算法以处理大规模数据?
对于非常大的网格尺寸,我们可以考虑以下几种优化策略:1. 使用迭代而非递归方法,通过迭代计算动态规划表,避免递归的深度限制和栈溢出的问题。2. 采用空间优化技术,例如只存储动态规划表中的当前行和前一行,因为计算当前行值只需要前一行的数据。这可以将空间复杂度从O(m*n)降低到O(min(m,n))。3. 如果问题允许,还可以使用并行处理技术,利用多线程或分布式计算来加速整个计算过程。

相关问题

不同路径 II

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

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

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 10 来表示。

 

示例 1:

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

示例 2:

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

 

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j]01

最小路径和

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

 

示例 1:

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

示例 2:

输入:grid = [[1,2,3],[4,5,6]]
输出:12

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 200

地下城游戏

恶魔们抓住了公主并将她关在了地下城 dungeon右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。

骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。

有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。

为了尽快解救公主,骑士决定每次只 向右向下 移动一步。

返回确保骑士能够拯救到公主所需的最低初始健康点数。

注意:任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。

 

示例 1:

输入:dungeon = [[-2,-3,3],[-5,-10,1],[10,30,-5]]
输出:7
解释:如果骑士遵循最佳路径:右 -> 右 -> 下 -> 下 ,则骑士的初始健康点数至少为 7 。

示例 2:

输入:dungeon = [[0]]
输出:1

 

提示:

  • m == dungeon.length
  • n == dungeon[i].length
  • 1 <= m, n <= 200
  • -1000 <= dungeon[i][j] <= 1000