【LeetCode】剑指 Offer 47. 礼物的最大价值 p233 -- Java Version

题目链接:https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof/

1. 题目介绍(47. 礼物的最大价值)

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

以下面的棋盘为例,如果沿着带下划线的数字的线路(1、12、5、7、7、16、5),那么我们能拿到最大价值为 53 的礼物。

在这里插入图片描述

【测试用例】:
示例 1:

输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

【条件约束】:

提示

  • 0 < grid.length <= 200
  • 0 < grid[0].length <= 200

2. 题解

2.1 动态规划(原书题解)-- O(mn)

时间复杂度O(mn),空间复杂度O(mn)

解题思路】:
本题是一个典型的动态规划解题的问题,我们先用递归的思路进行分析。我们先定义第一个函数 f(i,j) 表示到达坐标为 (i, j) 的格子时能拿到的礼物总和的最大值。根据题目要求,我们有两种可能的途径到达坐标为 (i, j) 的格子:

  1. 通过格子 (i-1,j) – 向下
  2. 通过格子 (i, j-1) – 向右

……
所以 f(i,j) = max(f(i-1,j), f(i,j-1)) + grid[i][j];
……
实现策略】:
-在这里插入图片描述
……
下列解法,我们是对 dp 数组中的第一行和第一列进行了分开赋值,我们可以通过定义两个变量充当临时存储的作用,将循环合在一起,这样就不用像下列解法一样,将循环分为三部分了。
……
进一步优化】:
由于到达坐标为 (i,j) 的格子时能够拿到的礼物的最大价值只依赖坐标为 (i-1,j)(i,j-1) 的两个格子,因此 第i-2行 及更上面的格子礼物的最大价值实际上没有必要保存下来。
……
因此我们可以使用一个一维数组来替代下列代码中的二维数组 dp

扫描二维码关注公众号,回复: 14731813 查看本文章
class Solution {
    
    
    // Solution1:动态规划
    public int maxValue(int[][] grid) {
    
    
        // 无效输入判断
        int m = grid.length;
        int n = grid[0].length;
        if (m <= 0 || n <= 0) return 0;

        // 定义二维数组,存储计算结果
        int[][] dp = new int[m][n];
        // 为数组第一行赋值
        dp[0][0] = grid[0][0];
        for (int i = 1; i < n; i++) dp[0][i] = dp[0][i-1] + grid[0][i];

        // 为数组的第一列赋值
        for (int j = 1; j < m; j++) dp[j][0] = dp[j-1][0] + grid[j][0];

        for (int i = 1; i < m; i++){
    
    
            for (int j = 1; j < n; j++) 
                dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]) + grid[i][j]; 
        }

        return dp[m-1][n-1];
    }
}

在这里插入图片描述

优化思路】:用一维数组来替代二维数组
举个例子,在计算 f[1][1] 时,会用到 f[0][1],但是之后就不再用到了。那么干脆把 f[1][1] 记到 f[0][1] 中,这样对于 f[1][2] 来说,它需要的数据就在 f[0][1]f[0][2] 中。f[1][2] 算完后也可以同样记到 f[0][2] 中。所以只需要一个长为 n+1 的一维数组就够了。
……
优化之后,时间复杂度不变,空间复杂度变为 O(n)

在这里插入图片描述

如上图所示,通过计算,存储,覆盖的形式,我们就可以将上图中的二维数组改为一维数组实现。

class Solution {
    
    
    // Solution2:动态规划(二维改一维)
    public int maxValue(int[][] grid) {
    
    
        // 无效输入判断
        int m = grid.length;
        int n = grid[0].length;
        if (m <= 0 || n <= 0) return 0;

        // 使用一维数组替代二维数组
        int[] dp = new int[n];
        
        for (int i = 0; i < m; i++){
    
    
            for (int j = 0; j < n; j++) {
    
    
                // 初始化辅助存值变量
                int up = 0;
                int left = 0;

                // i > 0 表示第一行数据存在
                if (i > 0) up = dp[j];
                // j > 0 表示第一列数据存在
                if (j > 0) left = dp[j-1];

                dp[j] = Math.max(up, left) + grid[i][j]; 
            }    
        }
        return dp[n-1];
    }
}

在这里插入图片描述

3. 参考资料

[1] 教你一步步思考 DP!从回溯到递推到 O(1) 额外空间!(Python/Java/C++/Go)

猜你喜欢

转载自blog.csdn.net/qq_41071754/article/details/129994626