【leetcode】64. 最小路径和
题目描述
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
解题思路
整体思路就是动态规划,只不过根据是否能修改原数组,选择二维数组(原数组),或者一维数组(新数组)。
1. 可以修改原数组的方法
如果可以修改原数组,则从数组的(0,0)位置开始,依次更新当前(i,j)的最小路径和。
由于第0行和第0列无法从上边或者左边移动而来,因此要先单独处理第0行和第0列。
对于第0行,从下标为1的数字开始,每个数字的路径只能从左侧过来,因此其最短路径是唯一的,直接grid[0][j] += grid[0][j-1]。
同理,对于第0列,grid[i][0] += grid[i-1][0]。
之后,对于其他位置的数字grid[i][j],分别可以从上边、左边的路径到达,因此选择这两条路径中最小的,在加上当前元素值,即min{grid[i-1][j], grid[i][j-1]}+grid[i][j]。
最终返回二维数组右下角元素值grid[row-1][col-1]。
代码
class Solution {
public int minPathSum(int[][] grid) {
if(grid==null || grid[0]==null || grid.length<=0 || grid[0].length<=0)
return -1;
int row = grid.length;
int col = grid[0].length;
for(int i=1; i<row; i++){
grid[i][0] += grid[i-1][0];
}
for(int j=1; j<col; j++){
grid[0][j] += grid[0][j-1];
}
for(int i=1; i<row; i++){
for (int j=1; j<col; j++){
grid[i][j] = Math.min(grid[i-1][j], grid[i][j-1]) + grid[i][j];
}
}
return grid[row-1][col-1];
}
}
2. 不能修改原数组,空间复杂度O(n)
如果不能在原数组上修改,则新维护一个长度为col(列数)的一维数组dp[]。dp[j]表示当前行的第j个元素的最短路径和。从第0行开始,每次处理该行所有列的元素,之后进入下一行继续处理。因为每次更新当前元素的最短路径时,仅需要左侧元素值与上方元素值,因此提前计算好left和up值即可。
对于非0行0列的元素,up值为上一轮的dp[j](因为还未更新过),left值为本轮的dp[j-1](已更新)。而对于第0行元素,up值初始化为Integer.MAX_VALUE,目的是在选择min{up,left}时,忽略不存在的up路径。对于第0列元素同理。
另外,对于(0,0)元素单独处理,直接等于grid[0][0],不再进行选择叠加。
最后返回dp数组中最后一个元素。
代码
class Solution {
public int minPathSum(int[][] grid) {
if(grid==null || grid[0]==null || grid.length<=0 || grid[0].length<=0)
return -1;
int row = grid.length;
int col = grid[0].length;
int[] dp = new int[col];
for(int i=0; i<row; i++){
for(int j=0; j<col; j++){
int up = Integer.MAX_VALUE;
int left = Integer.MAX_VALUE;
if(i>0)
up = dp[j];
if(j>0)
left = dp[j-1];
if(i==0 && j==0)
dp[j] = grid[i][j];
else
dp[j] = Math.min(left, up)+grid[i][j];
}
}
return dp[col-1];
}
}