「力扣」第 64 题:最小路径和(中等)


「力扣」第 64 题:最小路径和(中等)

很简单的「动态规划」问题,熟悉「自底向上」求解问题的思路。

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

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

示例

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

方法:动态规划

每一步只能左移或者下移。题目中给出了 grid 非负整数,可以保证走得越长,sum 的值越大;只能向右走或者向下走,保证了在非负整数矩阵的情况下,sum 的最小值存在。在分析清楚以后,我们可以直接在 grid 矩阵上原地修改,直接给出动态规划的解法,十分简单,逻辑也很清晰,核心代码不超过 10 行。

状态很好定义,题目中问什么,状态就定义成什么:dp[i][j]

Java 代码:

public class Solution {

        // 直接在 grid 数组上操作

    public int minPathSum(int[][] grid) {
        if (grid == null) {
            return 0;
        }

        int m = grid.length;
        int n = grid[0].length;
        for (int i = 1; i < n; i++) {
            grid[0][i] = grid[0][i] + grid[0][i - 1];
        }
        for (int i = 1; i < m; i++) {
            grid[i][0] = grid[i][0] + grid[i - 1][0];
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                grid[i][j] = Math.min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j];
            }
        }
        return grid[m - 1][n - 1];
    }

    public static void main(String[] args) {
        int[][] grid = {{1, 2, 5}, {3, 2, 1}};
        Solution solution = new Solution();
        int minPathSum = solution.minPathSum(grid);
        System.out.println(minPathSum);
    }
}

Python 代码:

from typing import List


class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:

        m = len(grid)
        if m == 0:
            return 0
        n = len(grid[0])

        for col in range(1, n):
            # 第 0 行特殊处理,不要忘记了
            grid[0][col] += grid[0][col - 1]
        for row in range(1, m):
            grid[row][0] += grid[row - 1][0]
            for col in range(1, n):
                grid[row][col] += min(grid[row - 1][col], grid[row][col - 1])
        return grid[-1][-1]


if __name__ == '__main__':
    grid = [[1, 3, 1],
            [1, 5, 1],
            [4, 2, 1]]

    s = Solution()
    res = s.minPathSum(grid)
    print(res)
  • 技巧:表格复用

Java 代码:

public class Solution {

    public int minPathSum(int[][] grid) {
        if (grid == null) {
            return 0;
        }
        int m = grid.length;
        int n = grid[0].length;

        int[] dp = new int[n];
        dp[0] = grid[0][0];

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 && j > 0) {
                    dp[j] = grid[0][j] + dp[j - 1];
                } else if (j == 0 && i > 0) {
                    dp[j] = dp[j] + grid[i][0];
                } else if (i != 0) {
                    dp[j] = Math.min(dp[j], dp[j - 1]) + grid[i][j];
                }
            }
        }
        return dp[n - 1];
    }
}

复杂度分析

  • 时间复杂度:img,这里 imgimg 分别是矩阵的长和宽;
  • 空间复杂度:img

(本节完)


文章作者: liweiwei419
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 liweiwei419 !
评论
 上一篇
「力扣」第 70 题:爬楼梯 「力扣」第 70 题:爬楼梯
「力扣」第 70 题:爬楼梯 斐波拉契数列,画出树形结构,发现大量重叠子问题。「动态规划」告诉我们「自顶向上」求解问题的思路。 链接 英文地址 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有
下一篇 
「力扣」第 63 题:不同路径 II(中等) 「力扣」第 63 题:不同路径 II(中等)
「力扣」第 63 题:不同路径 II(中等) 这题是动态规划的经典问题:理解「无后效性」。 1、状态压缩:具体讲解一下,状态行是如何压缩的; 2、哨兵技巧,回避边界条件的讨论; 3、数学方法; 4、只能向下和只能向右,说明符合动态规划的无后
  目录