创建一个比原网格大一号的二维数组 dp 在动态规划中有几个作用:

  1. 边界条件:在动态规划中,我们通常需要定义一些基础情况,也就是所谓的边界条件。这些条件为算法提供了起始点。在这个问题中,dp[0][j] 和 dp[i][0] 表示到达网格顶部或左侧边缘的路径和,由于没有路径可以走,它们的值应该为 0。创建一个额外的行和列可以方便地定义这些边界条件而不需要额外的检查。
  2. 简化索引计算:通过创建一个额外的行和列,我们可以简化循环中的索引计算。在循环中,我们可以直接使用 i 和 j 作为索引,而不需要担心越界的问题。这样,dp[i][j] 可以直接访问当前元素,dp[i-1][j] 访问上方元素,dp[i][j-1] 访问左侧元素,而不需要进行额外的条件判断。
  3. 避免重复计算:在动态规划中,每个状态(在这个例子中是 dp[i][j])的值是基于之前状态的值计算得出的。通过创建一个额外的行和列,我们可以确保在计算 dp[i][j] 时,所有需要的状态(dp[i-1][j] 和 dp[i][j-1])都已经被计算过了。这保证了动态规划的顺序性和最优子结构的性质。

总的来说,创建一个比原网格大一号的二维数组 dp 是一种常见的技巧,它简化了动态规划的实现,使得算法更容易理解和实现,同时也保证了算法的正确性和效率。

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param grid int整型二维数组 
     * @return int整型
     */
    public int maxValue (int[][] grid) {
        // write code here
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m + 1][n + 1];
        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 - 1][j - 1];
        return dp[m][n];
    }
}

在Java中,数组的索引是从0开始的,所以在使用双层循环遍历数组时,通常我们会从0开始迭代到数组长度减1。但在你的代码中,你希望ij从0开始,那么你只需要将循环的起始条件从1改为0,并且不需要减去1来访问grid数组的最后一个元素。以下是修改后的代码:

public int maxValue(int[][] grid) {
    int m = grid.length;
    int n = grid[0].length;
    int[][] dp = new int[m + 1][n + 1];
    // 初始化dp数组的边界条件
    for (int i = 0; i <= m; i++) {
        dp[i][0] = 0;
    }
    for (int j = 0; j <= n; j++) {
        dp[0][j] = 0;
    }
    // 填充dp数组
    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 - 1][j - 1];
        }
    }
    return dp[m][n];
}

在这个修改后的代码中,我添加了两个循环来初始化dp数组的第一行和第一列,因为它们没有被后续的循环所覆盖。这是因为在动态规划中,我们通常需要定义一些基础情况,这里的基础情况是dp[0][j]dp[i][0]对于所有的ij都应该是0,因为它们代表没有路径可以走的情况。

请注意,这段代码假设grid数组至少有一个元素,且grid[0]也至少有一个元素。如果gridgrid[0]可能为空,那么你需要在代码中添加额外的检查来处理这些情况。