【leetcode】741 摘樱桃(动态规划)

题目链接:https://leetcode-cn.com/problems/cherry-pickup/题目描述一个N x N的网格(grid) 代表了一块樱桃地,每个格子由以下三种数字的一种来表示:0 表示这个格子是空的,所以你可以穿过它。1 表示这个格子里装着一个樱桃,你可以摘到樱桃然后穿过它。-1 表示这个格子里有荆棘,挡着你的路。你的任务是在遵守下列规则的情况下...
摘要由CSDN通过智能技术生成

题目链接: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,

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值