剑指 Offer 47. 礼物的最大价值
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
解题思路:
根据题意我们可以得到信息,从左上角到右下角路过的每个位置的和最大,也就是我们要求的最大价值的礼物。
本体属于动态规划的题,就是下一步要进行什么与上一步的结果有关系,本题跟最经典的0-1背包问题其实是差不多的。
我们需要创建一个dp数组,大小跟原数组大小相等
算法流程:
动态规划:(动态规划五部曲)
确定dp数组以及下标的含义
dp[i] [j]的定义为:第(i,j)位置能得到最大礼物价值。
确定递推公式
我们可以从两个方向推出来dp[i] [j],推出dp[i] [j] 与dp[i] [j] 上方以及dp[i] [j] 左边的值有关,当dp[i] [j] 上方值大于左边值,那么dp[i] [j] = dp[i - 1] [j] + grid[i] [j];
所以递推公式: dp[i] [j] = Math.max(dp[i] [j - 1], dp[i - 1] [j]) + grid[i] [j];
dp数组如何初始化
针对于第一行跟第一列,每走到下一个位置,下一个位置最大礼物价值就是该位置与上一个位置数相加。(因为第一行只能一直向右走,第一列只能一直向下走。)
对于其他位置:
我们可以看递推公式:dp[i] [j] = Math.max(dp[i] [j - 1], dp[i - 1] [j]) + grid[i] [j]; 很明显每个位置与上方或左边位置的值有关,还有最初数组该位置有关,dp数组这个位置没关系,所以初始化为几都可以,当然我们为了方便直接初始化为0;
初始化数组如图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/11bc7fcf4fe16b4015a707e0273204cf.png)
确定遍历顺序
本题遍历可以先遍历行再遍历列,也可以先遍历列再遍历行。
针对我们日常习惯,我选择先遍历行。
for(inti=1; i<grid.length; i++){
for(intj=1; j<grid[0].length; j++){
dp[i][j] =Math.max(dp[i][j-1], dp[i-1][j]) +grid[i][j];
}
}
举例推导dp数组
来看一下对应的dp数组的数值,如图:
![](https://i-blog.csdnimg.cn/blog_migrate/7851308fbe7167c7abbcd36ffe407440.png)
最终结果就是dp[2] [2].
完整java代码
classSolution {
publicintmaxValue(int[][] grid) {
if(grid.length==0) return0;
int[][] dp=newint[grid.length][grid[0].length];
dp[0][0] =grid[0][0];
for(inti=1; i<grid[0].length; i++){
dp[0][i] =grid[0][i] +dp[0][i-1];
}
for(inti=1; i<grid.length; i++){
dp[i][0] =grid[i][0] +dp[i-1][0];
}
for(inti=1; i<grid.length; i++){
for(intj=1; j<grid[0].length; j++){
dp[i][j] =Math.max(dp[i][j-1], dp[i-1][j]) +grid[i][j];
}
}
returndp[grid.length-1][grid[0].length-1];
}
}
大致流程分析图:
![](https://i-blog.csdnimg.cn/blog_migrate/eaa546232f73fe3cae4c43486f432620.png)