轉載本文章請標明作者和出處
本文出自《Darwin的程序空間》
本文題目和部分解題思路來源自《劍指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]; } }