1. 题目分析
题目链接选自力扣 : 剑指 Offer 47. 礼物的最大价值
这个题, 如果抛开礼物价值不说的话, 和我们前面写的不同路径问题是一样的. 也是从左上角到右下角, 并且不能回头. 也就是无法向后或者向上走, 只能向下或者向右
来看看题目给的示例什么意思 :
它一共有 6 种不同路线从起点左上角到达终点右下角. 而这些不同路径经过的每一个点都对应一个礼物的价值. 计算不同路径到达终点时所获取的礼物价值最高.
从这 6 条路线来看, 很容易看出 浅蓝色这条线 1 -> 3 -> 5 -> 2 -> 1 = 12 这条路获得的礼物价值最贵为 12. 因此答案为最终到达终点最多能拿到价值 12 元的礼物
2. 状态表示
这还是一个二维的 dp 表. 以 dp[i][j] 表示从起点到 ( i, j ) 所获得的价值最高的礼物.
3. 状态转移方程
还是一样, 按照以往的经验我们以 ( i, j ) 位置的最近一步来表示.
当我们想要求解到达 ( i, j ) 位置的礼物最大价值时, 有两种情况.
- 从 ( i-1, j ) 位置往下走一步到达 ( i, j ) 位置
想要到达 ( i, j ) 点必须先到达 ( i-1, j ) 位置. 而从起点到达 ( i-1, j ) 位置只有一条路路 : A -> B -> C. 而最终经过这个点到达 ( i, j ) 点也只有一条路. 即 A -> B -> C -> F. 即到达 ( i-1, j ) 位置有多少条路径则从 ( i-1, j ) 到达 ( i, j ) 就有几条路径.
但我们要的是这些路径中的最大的礼物价值. 也就是从起点到达 ( i-1, j ) 的最高礼物价值. 正好对应到我们的状态表示中. 此时经过 ( i-1, j ) 到达 ( i, j ) 最大的礼物价值就是 dp[i-1][j] 的价值加上 ( i, j ) 位置本身的价值 grid[i][j]
- 从 ( i, j-1 ) 位置向前走一步到达 ( i, j ) 位置
同样的, 我要们要从 ( i, j-1 ) 位置到达 ( i, j ) 位置的最大价值也就为 dp[i][j-1] + grid[i, j]
最终我们的 dp[i][j] 是两种情况下获得的最大礼物价值. 即 dp[i][j] = Math.max( dp[i-1][j] , dp[i][j-1] ) + grid[i][j]
4. 初始化
初始化是为了保证进行填写 dp 表示不发生越界从而导致错误填表.
根据状态转移方程可以看到, 第一行和第一列上的每一个点, 它的上一步的礼物最大价值都是不知道的. 此时再去填表的时候就会越界发生错误. 因此我没是需要对这个原本的矩阵的第一行和第一列进行初始化的.
此处我们还是采取多开一行和多开一列的方式来做初始化. 这样可以把初始化放在填表中一起进行.
由于我们本身是根据每个位置的礼物价值来计算的. 起点的礼物价值在这个新的 dp 表中就是它本身 grid[i][j]. 因此我们新开的这一行和这一列只需要都为 0, 就能保证不会影响原本矩阵中每一行每一列的点的最终价值为当前位置的本身礼物价值.
虽然这样初始化可以了. 还是需要说明一点. 此处新开的每一行和每一列的值最好初始化为 负的无穷大. 为什么呢 ? 如果礼物价值为负数时, 我们当前这个初始化就会出问题从而影响填表的正确性
但是好在一点的是, 题目当中对礼物价值进行了限制, 是一个大于 0 的数.
5. 填表顺序
填表顺序想必也该知道了. 根据状态转移方程来看, 从上往下填写每一行, 而每一行又从左往右进行填写.
6. 返回值
题目要求的是返回从起点到 ( m, n ) 点的礼物最大价值. 而我们创建的 dp 表由于新开了一行一列变成 dp[m+1][n+1]. 因此最终答案对应在我们的 dp[m][n] 位置上. ( 注意下标的对应关系 )
7. 代码演示
class Solution { public int maxValue(int[][] grid) { // 1. 创建 dp 表 int m = grid.length; int n = grid[0].length; int[][] dp = new int[m+1][n+1]; // 2. 进行初始化 // 由于题目要求限制, 礼物是大于价值 0 的, 此处不进行初始化默认新开的一行和一列礼物价值就是 0. // 因此无需初始化 // 3. 填写 dp 表 for(int i = 1; i <= m; i++) { for(int j = 1; j <= n; j++) { // 根据状态转移方程填写 // 需要注意的是, 由于多开了一行一列. dp 表中的位置对应礼物表中的位置需要 -1 dp[i][j] = Math.max( dp[i-1][j] , dp[i][j-1] ) + grid[i - 1][j - 1]; } } // 4. 确认返回值 return dp[m][n]; } }