题目
给定一个包含非负整数的 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
概述
找出一条从左上角到右下角的路径,路径上数字之和最小。
方法 1: 暴力
暴力就是利用递归,对于每个元素我们考虑两条路径,向右走和向下走,在这两条路径中挑选路径权值和较小的一个。
c o s t ( i , j ) = g r i d [ i ] [ j ] + min ( c o s t ( i + 1 , j ) , c o s t ( i , j + 1 ) ) \mathrm{cost}(i, j)=\mathrm{grid}[i][j] + \min \big(\mathrm{cost}(i+1, j), \mathrm{cost}(i, j+1) \big) cost(i,j)=grid[i][j]+min(cost(i+1,j),cost(i,j+1))
public class Solution {
public int calculate(int[][] grid, int i, int j) {
if (i == grid.length || j == grid[0].length) return Integer.MAX_VALUE;
if (i == grid.length - 1 && j == grid[0].length - 1) return grid[i][j];
return grid[i][j] + Math.min(calculate(grid, i + 1, j), calculate(grid, i, j + 1));
}
public int minPathSum(int[][] grid) {
return calculate(grid, 0, 0);
}
}
复杂度分析
时间复杂度 :
O
(
2
m
+
n
)
O\big(2^{m+n}\big)
O(2m+n)。每次移动最多可以有两种选择。
空间复杂度 :O(m+n)。递归的深度是 m+n。
方法 2:二维动态规划
算法
我们新建一个额外的 dp 数组,与原矩阵大小相同。在这个矩阵中,dp(i, j) 表示从坐标 (i, j) 到右下角的最小路径权值。我们初始化右下角的 dp 值为对应的原矩阵值,然后去填整个矩阵,对于每个元素考虑移动到右边或者下面,因此获得最小路径和我们有如下递推公式:
d p ( i , j ) = g r i d ( i , j ) + min ( d p ( i + 1 , j ) , d p ( i , j + 1 ) ) dp(i, j)= \mathrm{grid}(i,j)+\min\big(dp(i+1,j),dp(i,j+1)\big) dp(i,j)=grid(i,j)+min(dp(i+1,j),dp(i,j+1))
注意边界情况。下图描述了这个过程:
public class Solution {
public int minPathSum(int[][] grid) {
int[][] dp = new int[grid.length][grid[0].length];
for (int i = grid.length - 1; i >= 0; i--) {
for (int j = grid[0].length - 1; j >= 0; j--) {
if(i == grid.length - 1 && j != grid[0].length - 1)
dp[i][j] = grid[i][j] + dp[i][j + 1];
else if(j == grid[0].length - 1 && i != grid.length - 1)
dp[i][j] = grid[i][j] + dp[i + 1][j];
else if(j != grid[0].length - 1 && i != grid.length - 1)
dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]);
else
dp[i][j] = grid[i][j];
}
}
return dp[0][0];
}
}
复杂度分析
时间复杂度 :O(mn)。遍历整个矩阵恰好一次。
空间复杂度 :O(mn)。额外的一个同大小矩阵。
方法 3:一维动态规划
算法
在上个解法中,我们可以用一个一维数组来代替二维数组,dp 数组的大小和行大小相同。这是因为对于某个固定状态,只需要考虑下方和右侧的节点。首先初始化 dp 数组最后一个元素是右下角的元素值,然后我们向左移更新每个 dp(j) 为:
d p ( j ) = g r i d ( i , j ) + min ( d p ( j ) , d p ( j + 1 ) ) dp(j)=\mathrm{grid}(i,j)+\min\big(dp(j),dp(j+1)\big) dp(j)=grid(i,j)+min(dp(j),dp(j+1))
我们对于每一行都重复这个过程,然后向上一行移动,计算完成后 dp(0) 就是最后的结果。
public class Solution {
public int minPathSum(int[][] grid) {
int[] dp = new int[grid[0].length];
for (int i = grid.length - 1; i >= 0; i--) {
for (int j = grid[0].length - 1; j >= 0; j--) {
if(i == grid.length - 1 && j != grid[0].length - 1)
dp[j] = grid[i][j] + dp[j + 1];
else if(j == grid[0].length - 1 && i != grid.length - 1)
dp[j] = grid[i][j] + dp[j];
else if(j != grid[0].length - 1 && i != grid.length - 1)
dp[j] = grid[i][j] + Math.min(dp[j], dp[j + 1]);
else
dp[j] = grid[i][j];
}
}
return dp[0];
}
}
复杂度分析
时间复杂度 :O(mn)。遍历整个矩阵恰好一次。
空间复杂度 :O(n)。额外的一维数组,和一行大小相同。
方法 4:动态规划(不需要额外存储空间)
算法
和方法 2 相同,惟一的区别是,不需要用额外的 dp 数组,而是在原数组上存储,这样就不需要额外的存储空间。递推公式如下:
g r i d ( i , j ) = g r i d ( i , j ) + min ( g r i d ( i + 1 , j ) , g r i d ( i , j + 1 ) ) \mathrm{grid}(i, j)=\mathrm{grid}(i,j)+\min \big(\mathrm{grid}(i+1,j), \mathrm{grid}(i,j+1)\big) grid(i,j)=grid(i,j)+min(grid(i+1,j),grid(i,j+1))
public class Solution {
public int minPathSum(int[][] grid) {
for (int i = grid.length - 1; i >= 0; i--) {
for (int j = grid[0].length - 1; j >= 0; j--) {
if(i == grid.length - 1 && j != grid[0].length - 1)
grid[i][j] = grid[i][j] + grid[i][j + 1];
else if(j == grid[0].length - 1 && i != grid.length - 1)
grid[i][j] = grid[i][j] + grid[i + 1][j];
else if(j != grid[0].length - 1 && i != grid.length - 1)
grid[i][j] = grid[i][j] + Math.min(grid[i + 1][j],grid[i][j + 1]);
}
}
return grid[0][0];
}
}
复杂度分析
时间复杂度 :O(mn)。遍历整个矩阵恰好一次。
空间复杂度 :O(1)。不需要额外空间。
作者:LeetCode
链接:https://leetcode-cn.com/problems/two-sum/solution/zui-xiao-lu-jing-he-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
感想
还是改62、63的代码…… 这几道题感觉有点重复了。
执行用时 :5 ms, 在所有Java提交中击败了80.13%的用户
内存消耗 :39.6 MB, 在所有Java提交中击败了86.58%的用户
class Solution {
public int minPathSum(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
for(int i=0;i<n;i++){
for(int j = 0;j<m;j++){
if(i==0&&j==0) continue;
if(i==0) grid[i][j]+=grid[i][j-1];
else if(j==0) grid[i][j]+=grid[i-1][j];
else grid[i][j]+=Math.min(grid[i-1][j],grid[i][j-1]);
}
}
return grid[n-1][m-1];
}
}