[LeetCode] 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 anN
byN
2D array, with1 <= 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,0,和1,其中-1表示障碍物不能通过,1表示有樱桃并可以通过,0表示没有樱桃并可以通过,并设定左上角为起点,右下角为终点,让我们从起点走到终点,再从终点返回起点,求最多能捡的樱桃的个数,限定起点和终点都没有障碍物。博主开始想的是就用dp来做呗,先从起点走到终点,求最多能捡多个樱桃,然后将捡起樱桃后将grid值变为0,然后再走一遍,把两次得到的樱桃数相加即可,但是类似贪婪算法的dp解法却跪在了下面这个case:
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 1
1 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 1 1 1
我们可以看出,红色的轨迹是第一次dp解法走过的路径,共拿到了13个樱桃,但是回到起点的话,剩下的两个樱桃无论如何也不可能同时拿到,只能拿到1颗,所以总共只能捡到14颗樱桃,而实际上所有的樱桃都可以捡到,需要换个走法的话,比如下面这种走法:
1 1 1 0 0 0
0 0 0 0 0
0 0 1 0 0 1
1 0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 1 1 1
红色为从起点到终点的走法,共拿到9颗樱桃,回去走蓝色的路径,可拿到6颗樱桃,所以总共15颗都能收入囊中。那这是怎么回事,原因出在了我们的dp递推式的设计上,博主之前设计式,当前位置的樱桃数跟上边和左边的樱桃数有关,取二者的较大值,如果只是从起点到终点走单程的话,这种设计是没有问题的,可以拿到最多的樱桃,但如果是round trip的话,那么就不行了。这里参考的还是fun4LeetCode大神的帖子,范佛利特扣德大神的帖子每次讲解都写的巨详细,总是让博主有种读paper的感觉。博主就挑选部分来讲讲,完整版可以自己去读一读大神的亲笔~
最开始时博主定义的dp[i][j]为单程的,即到达(i, j)位置能捡到的最大樱桃数,即:
T(i, j) = grid[i][j] + max{ T(i-, j), T(i, j-) }
但是定义单程就得改变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 : (, ) ==> (i-, j) ==> (i, j); (p, q) ==> (p-, q) ==> (, )
Case : (, ) ==> (i-, j) ==> (i, j); (p, q) ==> (p, q-) ==> (, )
Case : (, ) ==> (i, j-) ==> (i, j); (p, q) ==> (p-, q) ==> (, )
Case : (, ) ==> (i, j-) ==> (i, j); (p, q) ==> (p, q-) ==> (, )
根据定义,我们有:
Case is equivalent to T(i-, j, p-, q) + grid[i][j] + grid[p][q];
Case is equivalent to T(i-, j, p, q-) + grid[i][j] + grid[p][q];
Case is equivalent to T(i, j-, p-, q) + grid[i][j] + grid[p][q];
Case is equivalent to T(i, j-, p, q-) + grid[i][j] + grid[p][q];
因此,我们的重现关系可以写作:
T(i, j, p, q) = grid[i][j] + grid[p][q] + max{T(i-, j, p-, q), T(i-, j, p, q-), T(i, j-, p-, q), T(i, j-, p, q-)}
为了避免重复计算,我们希望 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-, i-, p-), T(k-, i-, p), T(k-, i, p-), T(k-, 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 = * n - ;
vector<vector<int>> dp(n, vector<int>(n, -));
dp[][] = grid[][];
for (int k = ; k < mx; ++k) {
for (int i = n - ; i >= ; --i) {
for (int p = n - ; p >= ; --p) {
int j = k - i, q = k - p;
if (j < || j >= n || q < || q >= n || grid[i][j] < || grid[p][q] < ) {
dp[i][p] = -;
continue;
}
if (i > ) dp[i][p] = max(dp[i][p], dp[i - ][p]);
if (p > ) dp[i][p] = max(dp[i][p], dp[i][p - ]);
if (i > && p > ) dp[i][p] = max(dp[i][p], dp[i - ][p - ]);
if (dp[i][p] >= ) dp[i][p] += grid[i][j] + (i != p ? grid[p][q] : );
}
}
}
return max(dp[n - ][n - ], );
}
};
类似题目:
参考资料:
https://discuss.leetcode.com/topic/112877/annotated-c-dp-solution
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] Cherry Pickup 捡樱桃的更多相关文章
- [LeetCode] 741. Cherry Pickup 捡樱桃
In a N x N grid representing a field of cherries, each cell is one of three possible integers. 0 mea ...
- [Swift]LeetCode741. 摘樱桃 | Cherry Pickup
In a N x N grid representing a field of cherries, each cell is one of three possible integers. 0 mea ...
- LeetCode 741. Cherry Pickup
原题链接在这里:https://leetcode.com/problems/cherry-pickup/ 题目: In a N x N grid representing a field of che ...
- LeetCode741. Cherry Pickup
https://leetcode.com/problems/cherry-pickup/description/ In a N x N grid representing a field of che ...
- 741. Cherry Pickup
In a N x N grid representing a field of cherries, each cell is one of three possible integers. 0 mea ...
- 动态规划-Cherry Pickup
2020-02-03 17:46:04 问题描述: 问题求解: 非常好的题目,和two thumb其实非常类似,但是还是有个一点区别,就是本题要求最后要到达(n - 1, n - 1),只有到达了(n ...
- Swift LeetCode 目录 | Catalog
请点击页面左上角 -> Fork me on Github 或直接访问本项目Github地址:LeetCode Solution by Swift 说明:题目中含有$符号则为付费题目. 如 ...
- [LeetCode] Minimum Cost to Merge Stones 混合石子的最小花费
There are N piles of stones arranged in a row. The i-th pile has stones[i] stones. A move consists ...
- leetcode动态规划题目总结
Hello everyone, I am a Chinese noob programmer. I have practiced questions on leetcode.com for 2 yea ...
随机推荐
- java性能调优---------------------JVM调优方案
JVM的调优的主要过程有: 1.确定堆内存大小(-Xmx.-Xms) 2.合理分配新生代和老年代(-XX:NewRatio.-Xmn.-XX:SurvivorRatio) 3.确定永久区大小(-XX: ...
- oracle exp(expdp)数据迁移(生产环境,进行数据对比校验)
前言:客户需要迁移XX 库 ZJJJ用户(迁移到其他数据库),由于业务复杂,客户都弄不清楚里面有哪些业务系统,为保持数据一致性,需要停止业务软件,中间件,杀掉oracle进程. 一.迁移数据倒出部分= ...
- 海外仓系统 COD货到付款到付功能
全球还有很多国家买家网购选择货到付款方式,例如东南亚的越南.泰国.印度尼西亚,中东的阿联酋.沙特等国家.在这些国家建立海外仓需要需要具备COD货到付款功能,麦哲伦海外仓系统已经支持COD货到到付结算相 ...
- Alpha冲刺No.7
一.站立式会议 彻底完成初步的界面设计 实现界面的简单跳转 完成部分事件监听 移植摄像头.图库功能到真实手机环境测试 数据库上传获取日记 二.项目实际进展 完成了简单的界面设计 大致完成了跳转任务 数 ...
- centos7下搭建sentry错误日志服务器
1. docker 安装(方法一) 1.确保yum packages 是最新的 $ sudo yum update 2.添加yum repo $ sudo tee /etc/yum.repos.d/d ...
- github上传时出现error: src refspec master does not match any解决办法
github上传时出现error: src refspec master does not match any解决办法 这个问题,我之前也遇到过,这次又遇到了只是时间间隔比较长了,为了防止以后再遇到类 ...
- Twisted UDP编程技术
实战演练1:普通UDP UDP是一种无连接对等通信协议,没有服务器和客户端概念,通信的任何一方均可通过通信原语直接和其他方通信 1.相对于TCP,UDP编程只需定义DatagramProtocol子类 ...
- Flask 学习 六 大型程序结构
pip freeze >requirement.txt 自动生成版本号 pip install -r requirement.txt 自动下载对应的库 梳理结构 config.py #!/usr ...
- 技术文档分享_linux中生成考核用的GPT分区表结构修复
注:历史版本,后期改用python实现了 实验一: 目的:用于生成大量模拟破坏GPT分区结构案例,并生成唯一方式修复后的评判方法.故障:在一个完整的GPT分区磁盘上,丢失了GPT主分区表,或备份分区表 ...
- SpringMVC之HandlerMapping的使用
上篇博客在了解SpringMVC的工作流程时留了一些疑问,今天先学习下HandlerMapping,在HandlerMapping中可以通过HandlerExecutionChain getHandl ...