持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
题目:在一个 m*n的棋盘上,棋盘的每一格都放有一个物品,每个物品的价值都大于 0。现在规定你可以从棋盘的左上角来开始拿格子里的物品,并且每次只能向右或者向下移动一格,直到最终到达棋盘的右下角。给定一个m*n的棋盘及其上面的物品价值,要求计算最终最多能拿到多少价值的物品?
解题思路
这里看到了棋盘,并且规定只能向棋盘的右边和下边移动,很像之前计算岛屿的那题,通过DFS搜索可以得到所有的情况,最终保留符合条件的结果即可。拿到本题我最开始的思路也是DFS,题目要去最终要到达棋盘的右下角,则代表只有最终的坐标满足右下角是符合要求的,此时可以计算结果并且结束DSF,并且当索引越界的情况也可以直接结束DFS,根据此思路可得如下代码:
private int maxValue = 0;
public int maxValue(int[][] grid) {
DFS(grid, 0, 0, 0);
return maxValue;
}
public void DFS(int[][] grid, int i, int j, int currValue){
if(i==grid.length-1&&j==grid[0].length-1){
maxValue = Math.max(maxValue, currValue+grid[i][j]);
return;
}
if(i==grid.length||j==grid[0].length) return;
int value = currValue+grid[i][j];
DFS(grid, i+1, j, value);
DFS(grid, i, j+1, value);
}
思路应该是没问题,跑了好几个测试用例都过了,但最终的结果是超时,也就意味着有更简单的方法来解决本题。我们再次看本题,因为题目限制我们只能往下和往右走,那也就意味着走到的格子的最大值只可能是由其左边的格子或者上边的格子到达,再加上自身的值即为可到达的最大值,这也就是最典型的动态规划,其转移方程为:
但当涉及边界的时候我们需要进行讨论,可得动态规划代码如下:
public int maxValue(int[][] grid) {
int[][] dp = new int[grid.length][grid[0].length];
dp[0][0] = grid[0][0];
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[0].length;j++){
if(i>0&&j>0){
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]) + grid[i][j];
}else if(j>0){
dp[i][j] = dp[i][j-1] + grid[i][j];
}else if(i>0){
dp[i][j] = dp[i-1][j] + grid[i][j];
}
}
}
return dp[grid.length-1][grid[0].length-1];
}
上述的判断是根据边界来进行划分,也可以使用下面的思路来消除边界的影响,既然我们需要判断当前的索引是否满足dp数组,那可以直接在dp数组外面再套一层,这就不存在边界越界的情况了,具体可看如下代码:
public int maxValue(int[][] grid) {
int[][] dp = new int[grid.length+1][grid[0].length+1];
for(int i=1;i<grid.length;i++) {
for (int j = 1; j < grid[0].length; j++) {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1])+grid[i-1][j-1];
}
}
return dp[grid.length][grid[0].length];
}