题目链接:https://leetcode-cn.com/problems/cherry-pickup/
题目描述
一个N x N的网格(grid) 代表了一块樱桃地,每个格子由以下三种数字的一种来表示:
-
0 表示这个格子是空的,所以你可以穿过它。
-
1 表示这个格子里装着一个樱桃,你可以摘到樱桃然后穿过它。
-
-1 表示这个格子里有荆棘,挡着你的路。
你的任务是在遵守下列规则的情况下,尽可能的摘到最多樱桃:
- 从位置 (0, 0) 出发,最后到达 (N-1, N-1) ,只能向下或向右走,并且只能穿越有效的格子(即只可以穿过值为0或者1的格子);
- 当到达 (N-1, N-1) 后,你要继续走,直到返回到 (0, 0) ,只能向上或向左走,并且只能穿越有效的格子;
- 当你经过一个格子且这个格子包含一个樱桃时,你将摘到樱桃并且这个格子会变成空的(值变为0);
- 如果在 (0, 0) 和 (N-1, N-1) 之间不存在一条可经过的路径,则没有任何一个樱桃能被摘到。
示例 1:
输入: grid =
[[0, 1, -1],
[1, 0, -1],
[1, 1, 1]]
输出: 5
解释:
玩家从(0,0)点出发,经过了向下走,向下走,向右走,向右走,到达了点(2, 2)。
在这趟单程中,总共摘到了4颗樱桃,矩阵变成了[[0,1,-1],[0,0,-1],[0,0,0]]。
接着,这名玩家向左走,向上走,向上走,向左走,返回了起始点,又摘到了1颗樱桃。
在旅程中,总共摘到了5颗樱桃,这是可以摘到的最大值了。
说明:
- grid 是一个 N * N 的二维数组,N的取值范围是1 <= N <= 50。
- 每一个
grid[i][j]
都是集合 {-1, 0, 1}其中的一个数。 - 可以保证起点
grid[0][0]
和终点grid[N-1][N-1]
的值都不会是 -1。
思路
题目的详解见英文leetcode原帖:https://leetcode.com/problems/cherry-pickup/discuss/109903/Step-by-step-guidance-of-the-O(N3)-time-and-O(N2)-space-solution
因为题目限制了grid[i][j]=1
的节点只能访问一次,所以核心的问题在于要避免partI和partII重复计数。
I 暴力尝试
暴力回溯,指数级时间复杂度。每趟来回 (4N-4)
步,可能的往返数 2^(4N-4)
。
II 动态规划尝试
(0, 0) ==> (N-1, N-1)
和 (N-1, N-1)==>(0,0)
分别使用动态规划得到其子问题最优解,但是总体 (0, 0) ==> (N-1, N-1) ==> (0, 0)
不一定是最优解。
grid = [[1,1,1,0,1],
[0,0,0,0,0],
[0,0,0,0,0],
[0,0,0,0,0],
[1,0,1,1,1]].
如上式在partI可以得到最优解,(0, 0) ==> (0, 2) ==> (4, 2) ==> (4, 4)
总和为6,对应的partII最多总和为1,总体来回总和为7。然后沿着矩形边缘可以得到所有的8个樱桃。所以这个贪心策略不一定能得到最优解。
III 修改grid矩阵的动态规划尝试
记录当前grid的状态,空间复杂度太高了
IV 最终版-不修改grid矩阵的动态规划
是否可以缩短路程,不需要走到右下角?YES!
我们重新定义 T(i,j)
为简化路程 (0, 0) ==> (i, j) ==> (0, 0)
可以得到的最大数,无需修改输入矩阵。此时原问题可以表示为T(N-1,N-1)
。为了得到递推关系式:
对于每个坐标 (i,j)
,我们有两种方式到达以及两种方式离开该点:(i-1, j)
和(i, j-1)
,往返路程可以分为以下四个case:
Case 1: (0, 0) ==> (i-1, j) ==> (i, j) ==> (i-1, j) ==> (0, 0)
Case 2: (0, 0) ==> (i, j-1) ==> (i, j) ==> (i, j-1) ==> (0, 0)
Case 3: (0, 0) ==> (i-1, j) ==> (i, j) ==> (i, j-1) ==> (0, 0)
Case 4: (0, 0) ==> (i, j-1) ==> (i, j) ==> (i-1, j) ==> (0, 0)
根据定义,case1等价于T(i-1, j) + grid[i][j]
,case2等价于T(i, j-1) + grid[i][j]
。但是我们定义的T(i, j)
并没有覆盖最后两种情形:PartI最后一步和PartII第一步不同。因此我们需要修改T(i,j)
定义,扩展为T(i, j, p, q)
,表明两段路程(0, 0) ==> (i, j); (p, q) ==> (0, 0)
的最大樱桃数,无需修改gird
矩阵。
类似上文所述,我们有两种方式到达坐标(i,j)
,两种方式离开坐标(p,q)
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)
根据定义得到:
Case 1 is equivalent to T(i-1,