题目
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:[ [1,3,1], [1,5,1], [4,2,1] ]
输出: 7
解释: 因为路径 1→3→1→1→1
的总和最小。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/minimum-path-sum 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
动态规划
很经典的动态规划题,动态规划是一种优化方法,一般用于求最值,它适用于后一阶段最优取决于上一阶段最优的问题,这类问题一定具备最优子结构,通过子问题的最优值得到原问题的最值,动态规划的过程实际上也是一个填表的过程。
对于该题,需要求从左上角到右下角的最小路径,且只能向下或向右走,则大问题是从左上角走到右下角,大问题的上一阶段是两个子问题:
- q 1 q_1 q1:从左上角走到右下角的左边一个位置
- q 2 q_2 q2:从左上角走到右下角的上面一个位置
只要这两个子问题已知,则原问题的解就是
m
i
n
(
q
1
,
q
2
)
+
g
r
i
d
[
m
]
[
n
]
min(q_1, q_2)+grid[m][n]
min(q1,q2)+grid[m][n],同样的,子问题
q
1
q_1
q1和
q
2
q_2
q2又对应了各自的两个子问题,因此可以从最小的子问题开始,即从左上角走到左上角
,不断得到大问题的解,这个过程就是填表的过程,整个表填完则得到大问题的解
class Solution {
public int m, n;
public int minPathSum(int[][] grid) {
m = grid.length;
n = grid[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (i > 0 && j > 0) {
grid[i][j] = Math.min(grid[i][j - 1], grid[i - 1][j]) + grid[i][j];
} else {
if (i == 0 && j == 0)
continue;
if (i == 0)
// top row
grid[i][j] += grid[i][j - 1];
if (j == 0)
// left col
grid[i][j] += grid[i - 1][j];
}
}
}
return grid[m - 1][n - 1];
}
}
递归(超时)
正如上面介绍,原问题的解就是 m i n ( q 1 , q 2 ) + g r i d [ m ] [ n ] min(q_1, q_2)+grid[m][n] min(q1,q2)+grid[m][n],因此也可以使用递归法,但是效率比较低,超时。。。
class Solution {
public int nums[][], m, n, min;
public int minPathSum(int[][] grid) {
m = grid.length;
n = grid[0].length;
nums = new int[m][n];
min = Integer.MAX_VALUE;
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
nums[i][j] = grid[i][j];
return walk(0, 0);
}
public int walk(int i, int j) {
if (i >= m || j >= n)
return Integer.MAX_VALUE;
if (i == m - 1 && j == n - 1)
// arrive at right down
return nums[i][j];
// down or right
return Math.min(walk(i, j + 1), walk(i + 1, j)) + nums[i][j];
}
}