转载本文章请标明作者和出处
本文题目和部分解题思路来源自《剑指offer》第二版
题目
在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
-
示例
输入: [ [1,3,1], [1,5,1], [4,2,1] ] 输出: 12 解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
解题分析
这道题,我能想到的一共三种解法。
首先,这很类似与迷宫的问题,很显然可以使用递归来解决,我们可以把所有可能到达右下角最后一个元素的路线的权值和都计算出来,然后取最大的一个。这样的做法会超时,不适合这么做;
这种方式的时间复杂度为O(2^m+n),空间复杂度为为O(m+n)
第二种,这道题很显然是一个动态规划的问题,到达最后一个元素(n,m)的最长路径,要取决于到达(n - 1,m)和(n,m - 1)的元素的最长路径,如果要求不破坏原有的数据,我们可以再定义一个同样大小的二维数组,从最后一行开始,从后往前遍历所有的元素,每一个元素都记录右下角到这个节点的最长路径,最后返回(0,0)个元素就可以了。(一维数组也可以实现)
这种方式的时间复杂度为O(mn),空间复杂度为为O(mn)(一维数组的话空间复杂度为O(m))
如果不需要保留原来的数据结构,我们可以在题目中的二维数组直接修改,这样空间复杂度为O(1)
代码
ps:这里笔者使用的jdk为1.8
-
java实现(一),二维数组
class Solution { public int maxValue(int[][] grid) { int[][] dp = new int[grid.length][grid[0].length]; for (int i = grid.length - 1; i >= 0; i--) { for (int j = grid[0].length - 1; j >= 0; j--) { if (i < grid.length - 1 && j < grid[0].length - 1) { dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]) + grid[i][j]; } else if (i == grid.length - 1 && j < grid[0].length - 1) { dp[i][j] = dp[i][j + 1] + grid[i][j]; } else if (i < grid.length - 1 && j == grid[0].length - 1) { dp[i][j] = dp[i + 1][j] + grid[i][j]; } else { dp[i][j] = grid[i][j]; } } } return dp[0][0]; } }
-
java实现(二),原地修改
class Solution { public int maxValue(int[][] grid) { for (int i = grid.length - 1; i >= 0; i--) { for (int j = grid[0].length - 1; j >= 0; j--) { if (i == grid.length - 1 && j < grid[0].length - 1) { grid[i][j] += grid[i][j + 1]; } else if (i != grid.length - 1 && j == grid[0].length - 1) { grid[i][j] += grid[i + 1][j]; } else if (i != grid.length - 1 && j != grid[0].length - 1) { grid[i][j] += Math.max(grid[i][j + 1], grid[i + 1][j]); } } } return grid[0][0]; } }