记一次动态规划的采坑之旅, 741摘樱桃 https://leetcode.cn/problems/cherry-pickup/description/

本文主要是介绍记一次动态规划的采坑之旅, 741摘樱桃 https://leetcode.cn/problems/cherry-pickup/description/,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首次看题目时,发现是困难。立马想到了,动态规划。

再看题目, 摘樱桃,还要返回摘两次,求摘最多的樱桃。

大脑第一反应就是:

先使用动态规划,找到 0 0 到 n-1 n-1处走过的最大樱桃, 并记录路径path。

然后根据路径path,将摘过的樱桃置为0,表示已经被摘过了。 然后再次摘樱桃。

两次摘过的樱桃之和就是目标的结果。

嗯,应该是,那就开写。

func cherryPickup(grid [][]int) int {ans := 0n := len(grid)dp := make([][]int, 0)for i := 0; i < n; i++ {dp = append(dp, make([]int, n))}// 1 表示上一个路径是 上// 2 表示上一个路径是 左path := make([][]int, 0)for i := 0; i < n; i++ {path = append(path, make([]int, n))}// 记录首次dp的轨迹,开始第一次摘for i := 0; i < len(grid); i++ {for j := 0; j < len(grid[0]); j++ {if i == 0 && j == 0 {dp[0][0] = grid[0][0]continue}if grid[i][j] == -1 {dp[i][j] = -1continue}if (i - 1 < 0 || grid[i-1][j] == -1 || dp[i-1][j] == -1) && (j - 1 < 0 || grid[i][j-1] == -1 || dp[i][j-1] == -1) {dp[i][j] = -1continue}if i - 1 < 0 || grid[i-1][j] == -1 || dp[i-1][j] == -1 {dp[i][j] = dp[i][j-1] + grid[i][j]path[i][j] = 2continue}if j - 1 < 0 || grid[i][j-1] == -1 || dp[i][j-1] == -1 {dp[i][j] = dp[i-1][j] + grid[i][j]path[i][j] = 1continue}if dp[i][j-1] > dp[i-1][j] {path[i][j] = 2dp[i][j] = dp[i][j-1] + grid[i][j]} else {path[i][j] = 1dp[i][j] = dp[i-1][j] + grid[i][j]}}}ans += dp[n-1][n-1]if ans == -1 {return 0}// 回溯路径, 清理已经被摘过的樱桃ii, jj := n-1, n-1for path[ii][jj] != 0 {grid[ii][jj] = 0if path[ii][jj] == 1 {ii--} else {jj--}}// 第二次摘樱桃grid[0][0] = 0dp = make([][]int, 0)for i := 0; i < n; i++ {dp = append(dp, make([]int, n))}for i := 0; i < len(grid); i++ {for j := 0; j < len(grid[0]); j++ {if i == 0 && j == 0 {dp[0][0] = grid[0][0]continue}if grid[i][j] == -1 {dp[i][j] = -1continue}if (i - 1 < 0 || grid[i-1][j] == -1 || dp[i-1][j] == -1) && (j - 1 < 0 || grid[i][j-1] == -1 || dp[i][j-1] == -1) {dp[i][j] = -1continue}if i - 1 < 0 || grid[i-1][j] == -1 || dp[i-1][j] == -1 {dp[i][j] = dp[i][j-1] + grid[i][j]path[i][j] = 2continue}if j - 1 < 0 || grid[i][j-1] == -1 || dp[i][j-1] == -1 {dp[i][j] = dp[i-1][j] + grid[i][j]path[i][j] = 1continue}if dp[i][j-1] > dp[i-1][j] {path[i][j] = 2dp[i][j] = dp[i][j-1] + grid[i][j]} else {path[i][j] = 1dp[i][j] = dp[i-1][j] + grid[i][j]}}}// 将两次摘的樱桃数相加return ans + dp[n-1][n-1]
}

最后发现通过 53/59 , 差一点点点点。 其实差很多。

通过研究这个未通过的案例, 发现我虽然2次摘樱桃都是最大值, 但并不能证明最终采摘的樱桃数是最大的。

最后瞄了一眼答案, 来回摘2次樱桃数最多,又不能重复摘, 那找两个人同时摘不就好了吗。 

附上leetcode标准答案

func cherryPickup(grid [][]int) int {n := len(grid)f := make([][][]int, n*2-1)for i := range f {f[i] = make([][]int, n)for j := range f[i] {f[i][j] = make([]int, n)for k := range f[i][j] {f[i][j][k] = math.MinInt32}}}f[0][0][0] = grid[0][0]for k := 1; k < n*2-1; k++ {for x1 := max(k-n+1, 0); x1 <= min(k, n-1); x1++ {y1 := k - x1if grid[x1][y1] == -1 {continue}for x2 := x1; x2 <= min(k, n-1); x2++ {y2 := k - x2if grid[x2][y2] == -1 {continue}res := f[k-1][x1][x2] // 都往右if x1 > 0 {res = max(res, f[k-1][x1-1][x2]) // 往下,往右}if x2 > 0 {res = max(res, f[k-1][x1][x2-1]) // 往右,往下}if x1 > 0 && x2 > 0 {res = max(res, f[k-1][x1-1][x2-1]) // 都往下}res += grid[x1][y1]if x2 != x1 { // 避免重复摘同一个樱桃res += grid[x2][y2]}f[k][x1][x2] = res}}}return max(f[n*2-2][n-1][n-1], 0)
}func min(a, b int) int {if a > b {return b}return a
}func max(a, b int) int {if b > a {return b}return a
}作者:力扣官方题解
链接:https://leetcode.cn/problems/cherry-pickup/solutions/1656418/zhai-ying-tao-by-leetcode-solution-1h3k/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这篇关于记一次动态规划的采坑之旅, 741摘樱桃 https://leetcode.cn/problems/cherry-pickup/description/的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/964928

相关文章

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

leetcode-24Swap Nodes in Pairs

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode swapPairs(L

leetcode-23Merge k Sorted Lists

带头结点。 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/public class Solution {public ListNode mergeKLists

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点