leetcode
leetcode 3051 ~ 3100
最小路径和

最小路径和

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 28 ms, 内存: 18.3 MB


/*
 * 思路:
 * 1. 使用动态规划和Java Stream解决问题。
 * 2. 创建一个dp二维数组,其中dp[i][j]表示到达位置(i, j)的最小路径和。
 * 3. 初始化dp数组的第一个元素dp[0][0]为grid[0][0]。
 * 4. 填充dp数组的第一行和第一列。
 * 5. 使用嵌套循环填充剩余的dp数组,每个元素取决于其上方和左方元素的最小值加上当前grid的值。
 * 6. 返回dp数组的最后一个元素即为所求。
 */
import java.util.Arrays;
import java.util.stream.IntStream;

public class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        dp[0][0] = grid[0][0];
        IntStream.range(1, m).forEach(i -> dp[i][0] = dp[i - 1][0] + grid[i][0]);
        IntStream.range(1, n).forEach(j -> dp[0][j] = dp[0][j - 1] + grid[0][j]);
        IntStream.range(1, m).forEach(i -> 
            IntStream.range(1, n).forEach(j -> 
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
            )
        );
        return dp[m - 1][n - 1];
    }
}

解释

方法:

此题解采用动态规划的方法来求解最小路径和问题。定义一个二维数组 dp,其中 dp[i][j] 表示从左上角 (0, 0) 到位置 (i, j) 的最小路径和。初始化 dp[0][0] 为起点的值 grid[0][0]。对于网格的其他位置,如果位置在第一行,只能从左边的位置到达;如果位置在第一列,只能从上面的位置到达;对于其他位置,则需要考虑从左边或上面到达的路径中哪一条的路径和更小。最后,dp数组的最右下角的值即为整个网格的最小路径和。

时间复杂度:

O(m * n)

空间复杂度:

O(m * n)

代码细节讲解

🦆
在动态规划解法中,为什么选择从左到右,从上到下的顺序填充 dp 数组,而不是其他顺序?
在这个问题中,每个单元格的最小路径和依赖于其左侧和上方的单元格的值(因为只能从左侧或上方到达)。因此,从左到右,从上到下的顺序确保在计算 dp[i][j] 时,dp[i-1][j](上方)和 dp[i][j-1](左侧)都已经被计算过,满足动态规划的依赖顺序。如果反向或交叉填充,会造成依赖的单元格还未被计算,从而无法正确计算当前单元格的值。
🦆
动态规划表格 dp 初始化时,只设置了 dp[0][0],为什么不需要初始化整个第一行和第一列?
代码中虽然初步只设置了 dp[0][0],但在遍历过程中对第一行和第一列的每个单元格都进行了单独的处理。对于第一行,每个单元格的值只依赖于其左边单元格的值;对于第一列,每个单元格的值只依赖于其上方单元格的值。这样的处理方式动态地在循环中初始化了第一行和第一列,无需事先设置整行或整列。
🦆
代码中对于边界情况的处理(第一行和第一列的情况),能否通过修改循环起始条件或添加额外的辅助行/列来简化代码?
可以通过添加额外的辅助行和列来简化代码。例如,可以设置 dp 数组的大小为 (rows+1) x (cols+1),并初始化辅助行和辅助列的值为极大数,除了 dp[1][1],这样就可以统一处理所有的单元格更新逻辑。每个单元格 dp[i][j] 都可以通过同样的方式计算,即 dp[i][j] = grid[i-1][j-1] + min(dp[i-1][j], dp[i][j-1])。这种方法减少了边界条件的特殊处理,使代码更加整洁。

相关问题