# 741. Cherry Pickup

**<font color=red>难度: Hard</font>**

## 刷题内容

> 原题连接

* https://leetcode.com/problems/cherry-pickup/

> 内容描述

```
In a N x N grid representing a field of cherries, each cell is one of three possible integers.

0 means the cell is empty, so you can pass through;
1 means the cell contains a cherry, that you can pick up and pass through;
-1 means the cell contains a thorn that blocks your way.
Your task is to collect maximum number of cherries possible by following the rules below:

Starting at the position (0, 0) and reaching (N-1, N-1) by moving right or down through valid path cells (cells with value 0 or 1);
After reaching (N-1, N-1), returning to (0, 0) by moving left or up through valid path cells;
When passing through a path cell containing a cherry, you pick it up and the cell becomes an empty cell (0);
If there is no valid path between (0, 0) and (N-1, N-1), then no cherries can be collected.
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:

grid is an N by N 2D array, with 1 <= N <= 50.
Each grid[i][j] is an integer in the set {-1, 0, 1}.
It is guaranteed that grid[0][0] and grid[N-1][N-1] are not -1.
```

## 解题方案

> 思路 1
******- 时间复杂度: O(N^3)******- 空间复杂度: O(N^3)******


可以看成两个人同时从(0, 0)出发到达(N-1, N-1)所能摘得的最多cherry，如果两个人到过同一个cherry点，注意要去重

记忆化搜索

beats 96.88%

```python
class Solution:
    def cherryPickup(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        N, cache = len(grid), {}
        
        def dp(r1, c1, c2):
            if (r1, c1, c2) in cache:
                return cache[(r1, c1, c2)]
            r2 = r1 + c1 - c2
            if r1 >= N or r2 >= N or c1 >= N or c2 >= N or \
                    grid[r1][c1] == -1 or grid[r2][c2] == -1:
                return -float('inf')
            elif r1 == c1 == N - 1:
                return grid[r1][c1]
            else:
                tmp = grid[r1][c1] + (c1 != c2) * grid[r2][c2]
                tmp += max(dp(r1+1, c1, c2+1), dp(r1, c1+1, c2+1), 
                           dp(r1+1, c1, c2), dp(r1, c1+1, c2))
                cache[(r1, c1, c2)] = tmp
                return tmp

        return max(0, dp(0, 0, 0))
```


> 思路 2
******- 时间复杂度: O(N^3)******- 空间复杂度: O(N^3)******

Bottom up


```
Assupmtion:
    Go from (0, 0) -> (n-1, n-1) -> (0, 0) can be opt to two men go from (0, 0) -> (n-1, n-1) together, but when they go into
the same cell, the cur state can only be added 1 (use once)
Using DP to solve the problem:
    1.  dp[x1][y1][x2] to represent the largest ans we can get when first guy (marked as A) at(x1, y1) and second guy(marked as B) at (x2, x1 + y1 - x2)
        * because we can only go right and down, so we have x1 + y1 = x2 + y2
    2.  Induction: every time we calculate the maximum of :
        * dp[x1 - 1][y1][x2] : A down, B right
        * dp[x1][y1 - 1][x2] : A right, B right
        * dp[x1 - 1][y1][x2 - 1]: A down, B down
        * dp[x1][y1 - 1][x2 - 1]: A right, B down
        if the Max of these values is negative, then we don't have a path to this point
        else we have: dp[x1][y1][x2] = Max + grid[x1 - 1][y1 - 1] + grid[x2 - 1][y2 - 1](if x1 != x2 && y1 != y2) else we
        only add once.
    3.  Base case;
        we use dp[][][]from 1 - n, so we have:
            dp[1][1][1] = 1 and all other values are MIN_VALUE
    4.  Ans:
        dp[n][n][n]
    5.  Direction:
        from top left -> bottom right
    6.  Time:
        O(n^3)
        Space:
        O(n^3)
        
```

摘自[Java O(N^3) DP solution w/ specific explanation](https://leetcode.com/problems/cherry-pickup/discuss/165218/Java-O(N3)-DP-solution-w-specific-explanation)

beats 56.25%

```python
class Solution:
    def cherryPickup(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        N = len(grid)
        dp = [[[-float('inf')] * (N+1) for i in range(N+1)] for j in range(N+1)]
        dp[1][1][1] = grid[0][0]
        
        for x1 in range(1, N+1):
            for y1 in range(1, N+1):
                for x2 in range(1, N+1):
                    if x1 == y1 == x2 == 1: # 初始值不能改变
                        continue
                    y2 = x1 + y1 - x2 # x1 + y1 = x2 + y2
                    # have already detected || out of boundary || cannot access 
                    if dp[x1][y1][x2] > 0 or y2 < 1 or y2 > N or \
                            grid[x1-1][y1-1] == -1 or grid[x2-1][y2-1] == -1:
                        continue
                    cur = max(dp[x1-1][y1][x2], dp[x1-1][y1][x2-1],
                                dp[x1][y1-1][x2], dp[x1][y1-1][x2-1])
                    dp[x1][y1][x2] = cur + grid[x1 - 1][y1 - 1] + (x1 != x2) * grid[x2 - 1][y2 - 1]
                    
        return max(0, dp[-1][-1][-1])
```





> 思路 3
******- 时间复杂度: O(N^3)******- 空间复杂度: O(N^2)******


压缩空间, 参考[solution3](https://leetcode.com/problems/cherry-pickup/solution/)

```python
class Solution:
    def cherryPickup(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        N = len(grid)
        dp = [[float('-inf')] * N for _ in range(N)]
        dp[0][0] = grid[0][0]
        for t in range(1, 2 * (N-1) + 1):
            dp2 = [[float('-inf')] * N for _ in range(N)]
            for i in range(max(0, t-(N-1)), min(N-1, t) + 1): # r1 = t-c1, r2 = t-c2
                for j in range(max(0, t-(N-1)), min(N-1, t) + 1):
                    if grid[i][t-i] == -1 or grid[j][t-j] == -1:
                        continue
                    val = grid[i][t-i] + (i != j) * grid[j][t-j]
                    dp2[i][j] = max(dp[pi][pj] + val
                                    for pi in (i-1, i) for pj in (j-1, j)
                                    if pi >= 0 and pj >= 0)
            dp = dp2
        return max(0, dp[N-1][N-1])
```
























