动态规划之最小路径和

简介: 动态规划之最小路径和

1. 题目分析



题目链接选自力扣 : 最小路径和


44112be4cf9ccb9dd8a9f6e4f917b036.png


前面解触了很多路径题. 这依然是一个路径题, 题目要求从左上角到达右下角, 期间每次只能向右或者向下走一步, 求最终路径总和最小值.


2. 状态表示



以 ( i, j ) 位置结尾, 从起始位置到达 ( i, j ) 位置的最小路径和即为 dp[i][j]


3. 状态转移方程



根据之前的经验, 利用最近一步来划分问题. 对于路径问题想必已经是很熟悉了

0f6b3a7037a9ffe57858133a6a926fa3.png


  1. 从 ( i-1, j ) 位置到达 ( i, j ) 位置


从起始位置到达 ( i-1, j ) 位置后再多走一步到达 ( i,j ) 位置. 期间不同的路径到达 ( i-1, j ) 有不同的路径和, 选择最小的路径和后再多走一步到达 ( i, j ) 位置. 则此时最小路径和为到达 ( i-1, j ) 位置的最小路径和 dp[i-1][j] 在多走一步加上 grid[i][j]


  1. 从 ( i, j-1 ) 位置到达 ( i, j) 位置


同理, 此时最小路径和为 dp[i][j-1] + grid[i][j]


我们找的是到达 ( i, j ) 位置时的最下路径和, 因此是取这两种情况的最小值. 最终的状态转移方程为 : dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j]


4. 初始化



初始化为了保证状态转移方程填表时不越界. 根据状态转移方程可以看出, 这些位置是需要进行初始化的, 否则填表时会发生错误.

image.png


还是用新开一行一列的方式进行初始化, 通过外部新增的一行和一列来保证原本需要初始化的第一行和第一列在填表时正确.

image.png


在这题中, 初始化有一些细节情况

  1. 当填写下面这个位置时, 可以知道它受到我们新开的一行和一列中的两个影响. 根据动态转移方程,这个位置最终应该初始化为它本身的值. 因此就要求它上面和前面的位置不能影响它. 进而推导出这两个位置的值应该为 0

image.png


当填写下面这个位置时, 它受到这两个位置的影响. 根据状态转移方程取的是这两个位置的最小值. 但是新开的这个位置是为了来初始化它的不应该影响它的取值. 因此当这个位置取位正无穷时, 最小值就只能取这个位置的上方. 只受上方位置的影响.

image.png


最终我们发现, 除了起始位置的上方和后方初始化为 0, 其余新增的位置都应该初始化为无穷大才能保证填表时数据的正确性.

image.png


5. 填表顺序



根据状态转移方程, 想填写某个位置时, 需要先知道它上面一个位置和前一个位置的值. 因此填表的顺序为 从上往下填写每一行, 每一行从左往右填写.


6. 返回值



根据题目要求, 返回从起始点到达终点 ( m, n ) 的位置的路径最小和. 由于我们新开了一行一列, 对应到我们的状态表示中为 dp[m][n] ( 注意下标关系 )


7. 代码演示



class Solution {
    public int minPathSum(int[][] grid) {
        // 1. 创建 dp 表
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m + 1][n + 1];
        // 防止后续初始化 dp[1][0] 或者 dp[0][1] 时越界
        // 此处只能用 &&, 当 m = 1 或 n = 1时, 只能代表该二维数组为一行或一列
        // 一行时, 起始位置为第一个元素, 终点为最后一个元素
        // 一列时, 同样起点为第一个元素, 终点为这一列最后一个元素
        // 这里是针对二维数组只有一个元素时, 直接返回这个元素
        if(m == 1 && n == 1) {
            return grid[0][0];
        }
        // 2. 初始化
        // 先将新增的第一行和第一列全部初始化为无穷大
        for(int j = 0; j <= n; j++) {
            dp[0][j] = Integer.MAX_VALUE;
        }
        for(int i = 0; i <= m; i++) {
            dp[i][0] = Integer.MAX_VALUE;
        }
        // 将起始位置的上方和前方初始化为 0
        dp[0][1] = dp[1][0] = 0;
        // 3. 填写 dp 表
        for(int i = 1; i <= m; i ++) {
            for(int j = 1; j <= n; j++) {
                // 根据状态方程填写
                // 由于我们新开了一行一列. 此时的位置对应到grid 中下标要 - 1
                dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i - 1][j - 1];
            }
        }
        // 4. 确认返回值
        return dp[m][n];
    }
}


相关文章
|
6月前
|
消息中间件 Kubernetes NoSQL
动态规划-状态压缩、树形DP问题总结
动态规划-状态压缩、树形DP问题总结
|
6月前
|
机器学习/深度学习 消息中间件 Kubernetes
动态规划-线性DP问题总结(一)
动态规划-线性DP问题总结(一)
|
算法
【学会动态规划】最小路径和(9)
【学会动态规划】最小路径和(9)
41 0
|
算法
【学会动态规划】最大子数组和(19)
【学会动态规划】最大子数组和(19)
47 0
|
6月前
|
消息中间件 Kubernetes NoSQL
动态规划-线性DP问题总结(二)
动态规划-线性DP问题总结(二)
动态规划:完全背包问题
动态规划:完全背包问题
74 0
动态规划-完全背包
前言 我们上篇文章学习了动态规划01背包的问题,本篇文章我们继续学习完全背包。大家可以在学习完01背包的基础上再进行学习,比较其两者的解题差异。
爬楼梯(动态规划)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
66 0
|
存储
三角形最小路径和(动态规划)
给定一个三角形 triangle ,找出自顶向下的最小路径和。
106 0
三角形最小路径和(动态规划)
|
算法 测试技术
贪心——53. 最大子数组和
本专栏按照数组—链表—哈希—字符串—栈与队列—二叉树—回溯—贪心—动态规划—单调栈的顺序刷题,采用代码随想录所给的刷题顺序,一个正确的刷题顺序对算法学习是非常重要的,希望对大家有帮助
贪心——53. 最大子数组和