In a N x N grid
representing a field of cherries, each cell is one of three possible integers.html
Your task is to collect maximum number of cherries possible by following the rules below:算法
Example 1:數組
Input: grid = [[0, 1, -1], [1, 0, -1], [1, 1, 1]] Output: 5 Explanation: The player started at (0, 0) and went down, down, right right to reach (2, 2). 4 cherries were picked up during this single trip, and the matrix becomes [[0,1,-1],[0,0,-1],[0,0,0]]. Then, the player went left, up, up, left to return home, picking up one more cherry. The total number of cherries picked up is 5, and this is the maximum possible.
Note:post
grid
is an N
by N
2D array, with 1 <= N <= 50
.grid[i][j]
is an integer in the set {-1, 0, 1}
.
這道題給了咱們一個二維數組,每一個數字只有三個數字,-1,0,和1,其中-1表示障礙物不能經過,1表示有櫻桃並能夠經過,0表示沒有櫻桃並能夠經過,並設定左上角爲起點,右下角爲終點,讓咱們從起點走到終點,再從終點返回起點,求最多能撿的櫻桃的個數,限定起點和終點都沒有障礙物。博主開始想的是就用dp來作唄,先從起點走到終點,求最多能撿多個櫻桃,而後將撿起櫻桃後將grid值變爲0,而後再走一遍,把兩次獲得的櫻桃數相加便可,可是相似貪婪算法的dp解法卻跪在了下面這個case:ui
1 1 1 1 0 0 0
0 0 0 1 0 0 0
0 0 0 1 0 0 1
1 0 0 1 0 0 0
0 0 0 1 0 0 0
0 0 0 1 0 0 0
0 0 0 1 1 1 1
咱們能夠看出,紅色的軌跡是第一次dp解法走過的路徑,共拿到了13個櫻桃,可是回到起點的話,剩下的兩個櫻桃不管如何也不可能同時拿到,只能拿到1顆,因此總共只能撿到14顆櫻桃,而實際上全部的櫻桃均可以撿到,須要換個走法的話,好比下面這種走法:this
1 1 1 1 0 0 0
0 0 0 1 0 0 0
0 0 0 1 0 0 1
1 0 0 1 0 0 0
0 0 0 1 0 0 0
0 0 0 1 0 0 0
0 0 0 1 1 1 1
紅色爲從起點到終點的走法,共拿到9顆櫻桃,回去走藍色的路徑,可拿到6顆櫻桃,因此總共15顆都能收入囊中。那這是怎麼回事,緣由出在了咱們的dp遞推式的設計上,博主以前設計式,當前位置的櫻桃數跟上邊和左邊的櫻桃數有關,取兩者的較大值,若是隻是從起點到終點走單程的話,這種設計是沒有問題的,能夠拿到最多的櫻桃,但若是是round trip的話,那麼就不行了。這裏參考的仍是fun4LeetCode大神的帖子,範佛利特扣德大神的帖子每次講解都寫的巨詳細,老是讓博主有種讀paper的感受。博主就挑選部分來說講,完整版能夠本身去讀一讀大神的親筆~url
最開始時博主定義的dp[i][j]爲單程的,即到達(i, j)位置能撿到的最大櫻桃數,即:spa
T(i, j) = grid[i][j] + max{ T(i-1, j), T(i, j-1) }
可是定義單程就得改變grid的值,再進行一次dp計算時,就會陷入以前例子中的陷阱。因此咱們的dp[i][j]仍是須要定義爲round trip的,即到達(i, j)位置並返回起點時能撿到的最大櫻桃數,可是新的問題就來了,櫻桃只有一個,只能撿一次,去程撿了,返程就不能再撿了,如何才能避免重複計算呢?咱們只有i和j是不夠的,其只能定義去程的位置,咱們還須要pg,(不是pgone哈哈),來定義返程的位置,那麼重現關係Recurrence Relations就變成了 T(i, j, p, g),咱們有分別兩種方式離開(i, j)和(p, g),咱們suppose時從終點往起點遍歷,那麼就有4種狀況:設計
Case 1: (0, 0) ==> (i-1, j) ==> (i, j); (p, q) ==> (p-1, q) ==> (0, 0) Case 2: (0, 0) ==> (i-1, j) ==> (i, j); (p, q) ==> (p, q-1) ==> (0, 0) Case 3: (0, 0) ==> (i, j-1) ==> (i, j); (p, q) ==> (p-1, q) ==> (0, 0) Case 4: (0, 0) ==> (i, j-1) ==> (i, j); (p, q) ==> (p, q-1) ==> (0, 0)
根據定義,咱們有:code
Case 1 is equivalent to T(i-1, j, p-1, q) + grid[i][j] + grid[p][q]; Case 2 is equivalent to T(i-1, j, p, q-1) + grid[i][j] + grid[p][q]; Case 3 is equivalent to T(i, j-1, p-1, q) + grid[i][j] + grid[p][q]; Case 4 is equivalent to T(i, j-1, p, q-1) + grid[i][j] + grid[p][q];
所以,咱們的重現關係能夠寫做:
T(i, j, p, q) = grid[i][j] + grid[p][q] + max{T(i-1, j, p-1, q), T(i-1, j, p, q-1), T(i, j-1, p-1, q), T(i, j-1, p, q-1)}
爲了不重複計算,咱們但願 grid[i][j] 和 grid[p][g] 不出如今T(i-1, j, p-1, q), T(i-1, j, p, q-1), T(i, j-1, p-1, q) 和 T(i, j-1, p, q-1)中的任意一個上。顯而易見的是(i, j)不會出如今(0, 0) ==> (i-1, j) 或 (0, 0) ==> (i, j-1) 的路徑上,同理,(p, g) 也不會出如今 (p-1, q) ==> (0, 0) 或 (p, q-1) ==> (0, 0) 的路徑上。所以,咱們須要保證(i, j) 不會出如今 (p-1, q) ==> (0, 0) 或 (p, q-1) ==> (0, 0) 的路徑上,同時 (p, g)不會出如今(0, 0) ==> (i-1, j) 或 (0, 0) ==> (i, j-1) 的路徑上,怎麼作呢?
咱們觀察到(0, 0) ==> (i-1, j) 和 (0, 0) ==> (i, j-1) 的全部點都在矩形 [0, 0, i, j] 中(除了右下角點(i, j)點),因此只要 (p, g) 不在矩形 [0, 0, i, j] 中就好了,注意(p, g) 和 (i, j) 是有可能重合了,這種狀況特殊處理一下就好了。同理, (i, j) 也不能在矩形 [0, 0, p, g] 中,那麼如下三個條件中須要知足一個:
i < p && j > q i == p && j == q i > p && j < q
爲了知足上述條件,咱們但願當 i 或 p 增長的時候,j 或 q 減少,那麼咱們能夠有這個等式:
k = i + j = p + q
其中k爲從起點開始走的步數,因此咱們能夠用 T(k, i, p) 來代替 T(i, j, p, g),那麼咱們的重現關係式就變成了:
T(k, i, p) = grid[i][k-i] + grid[p][k-p] + max{T(k-1, i-1, p-1), T(k-1, i-1, p), T(k-1, i, p-1), T(k-1, i, p)}.
當 i == p 時,grid[i][k-i] 和 grid[p][k-p] 就相等了,此時只能加一個。咱們注意到 i, j, p, q 的範圍是 [0, n), 意味着k只能在範圍 [0, 2n - 1) 中, 初始化時 T(0, 0, 0) = grid[0][0]。咱們這裏的重現關係T雖然是三維的,可是咱們能夠用二維dp數組來實現,由於第k步的值只依賴於第k-1步的狀況,參見代碼以下:
class Solution { public: int cherryPickup(vector<vector<int>>& grid) { int n = grid.size(), mx = 2 * n - 1; vector<vector<int>> dp(n, vector<int>(n, -1)); dp[0][0] = grid[0][0]; for (int k = 1; k < mx; ++k) { for (int i = n - 1; i >= 0; --i) { for (int p = n - 1; p >= 0; --p) { int j = k - i, q = k - p; if (j < 0 || j >= n || q < 0 || q >= n || grid[i][j] < 0 || grid[p][q] < 0) { dp[i][p] = -1; continue; } if (i > 0) dp[i][p] = max(dp[i][p], dp[i - 1][p]); if (p > 0) dp[i][p] = max(dp[i][p], dp[i][p - 1]); if (i > 0 && p > 0) dp[i][p] = max(dp[i][p], dp[i - 1][p - 1]); if (dp[i][p] >= 0) dp[i][p] += grid[i][j] + (i != p ? grid[p][q] : 0); } } } return max(dp[n - 1][n - 1], 0); } };
相似題目:
參考資料:
https://discuss.leetcode.com/topic/112877/annotated-c-dp-solution