【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];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值