【动态规划】【C++算法】741摘樱桃

2024-01-20 10:52
文章标签 算法 动态 规划 c++ 741 樱桃

本文主要是介绍【动态规划】【C++算法】741摘樱桃,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者推荐

【动态规划】【数学】【C++算法】18赛车

涉及知识点

动态规划

LeetCode741 摘樱桃

给你一个 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 个樱桃,这是最大可能值。
示例 2:
输入:grid = [[1,1,-1],[1,-1,1],[-1,1,1]]
输出:0
参数范围
n == grid.length
n == grid[i].length
1 <= n <= 50
grid[i][j] 为 -1、0 或 1
grid[0][0] != -1
grid[n - 1][n - 1] != -1

动态规划

起程和返程可能进过同一地点,此处的樱桃只能摘一次。转换一下思路:假定有两个机器人,第一个机器按起程路线走,第二个机器人按返程路线反向走。由于只能向右下,所以不会存在环,也不会往回走。假定移动step次后,第一个机器人在r1行,第二个机器人在r2行,则第一个机器人在第step-c1列,第二个机器人在step-c2列。同一格行列号相同 ==> 行列号之和(step)相同 ==> 不同step,一定不是同一格子。
小技巧: setp相同时,只需要判断行号或列号是否相同。

动态规划的填表顺序

时间复杂度:O((n*2)nn) 三层循环,第一层从小到大枚举step;第二层枚举第一个机器人所在行,第二层枚举第二个机器人所在行。

动态规划的状态表示

pre[r1][r2]表示 step步后,机器人一在r1,机器人二在r2 已经收获的最大樱桃数。
dp[nr1][nr2]表示 step+1步后,机器人一在nr1,机器人二在nr2 已经收获的最大樱桃数。

动态规划的转移方程

int i2 = (nr1 == nr2) ? 0 : grid[nr2][nc2];//如果两个机器人 在同一位置不重复计算dp[nr1][nr2] = max(dp[nr1][nr2],pre[r1][r2] + grid[nr1][nc1] + i2 );

动态规划的初始状态

pre[0][0] = grid[0][0];

动态规划的返回值

return (-1==pre.back().back())?0: pre.back().back();

代码

封装类

class CEnumGridEdge
{
public:void Init(){for (int r = 0; r < m_r; r++){for (int c = 0; c < m_c; c++){Move(r, c, r + 1, c);Move(r, c, r - 1, c);Move(r, c, r, c + 1);Move(r, c, r, c - 1);}}}const int m_r, m_c;
protected:CEnumGridEdge(int r, int c) :m_r(r), m_c(c){}void Move(int preR, int preC, int r, int c){if ((r < 0) || (r >= m_r)){return;}if ((c < 0) || (c >= m_c)){return;}OnEnumEdge(preR, preC, r, c);};virtual void OnEnumEdge(int preR, int preC, int r, int c) = 0;
};

复制类

class TNeiBoForGrid :  public CEnumGridEdge
{
public:	TNeiBoForGrid(const vector<vector<int>>& grid):m_grid(grid),CEnumGridEdge(grid.size(),grid.front().size()){m_vNext.assign(m_r, vector < vector<pair<int, int>>>(m_c));Init();}virtual void OnEnumEdge(int preR, int preC, int r, int c){if ((-1 == m_grid[preR][preC]) || (-1 == m_grid[r][c])){return;}if ((preR > r) || (preC > c)){return;}m_vNext[preR][preC].emplace_back(r, c);}const vector<vector<int>>& m_grid;vector < vector < vector<pair<int, int>>>> m_vNext;
};

核心代码

class Solution {
public:int cherryPickup(vector<vector<int>>& grid) {TNeiBoForGrid neiBo(grid);vector<vector<int>> pre(neiBo.m_r, vector<int>(neiBo.m_r, -1));pre[0][0] = grid[0][0];for (int step = 0; step < neiBo.m_r + neiBo.m_c - 2; step++){vector<vector<int>> dp(neiBo.m_r, vector<int>(neiBo.m_r, -1));for(int r1 = max(0,step-(neiBo.m_c-1)) ; r1 <= min(step, neiBo.m_r-1);r1++ )for (int r2 = max(0, step - (neiBo.m_c - 1)); r2 <= min(step, neiBo.m_r-1); r2++){if (-1 == pre[r1][r2]){continue;}const int c1 = step - r1;const int c2 = step - r2;for (const auto& [nr1, nc1] : neiBo.m_vNext[r1][c1]){for (const auto& [nr2, nc2] : neiBo.m_vNext[r2][c2]){int i2 = (nr1 == nr2) ? 0 : grid[nr2][nc2];//如果两个机器人 在同一位置不重复计算dp[nr1][nr2] = max(dp[nr1][nr2],pre[r1][r2] + grid[nr1][nc1] + i2 );}}}pre.swap(dp);}return (-1==pre.back().back())?0: pre.back().back();}
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{vector<vector<int>> grid;{Solution sln;grid = { {-1} };auto res = sln.cherryPickup(grid);Assert(0, res);}{Solution sln;grid = { {0} };auto res = sln.cherryPickup(grid);Assert(0, res);}{Solution sln;grid = { {1} };auto res = sln.cherryPickup(grid);Assert(1, res);}{Solution sln;grid = { {0,1,-1},{1,0,-1},{1,1,1} };auto res = sln.cherryPickup(grid);Assert(5, res);}{Solution sln;grid = { {1,1,-1},{1,-1,1},{-1,1,1} };auto res = sln.cherryPickup(grid);Assert(0, res);}{Solution sln;grid = { {1, -1, 1, -1, 1, 1, 1, 1, 1, -1}, { -1,1,1,-1,-1,1,1,1,1,1 }, { 1,1,1,-1,1,1,1,1,1,1 }, { 1,1,1,1,1,1,1,1,1,1 }, { -1,1,1,1,1,1,1,1,1,1 }, { 1,-1,1,1,1,1,-1,1,1,1 }, { 1,1,1,-1,1,1,-1,1,1,1 }, { 1,-1,1,-1,-1,1,1,1,1,1 }, { 1,1,-1,-1,1,1,1,-1,1,-1 }, { 1,1,-1,1,1,1,1,1,1,1 } };auto res = sln.cherryPickup(grid);Assert(0, res);}}

2023年1月

class Solution {
public:
int cherryPickup(vector<vector>& grid) {
m_c = grid.size();
m_preDp.assign(1, vector(1, grid[0][0]));
for (int len = 1; len <= m_c * 2 - 2; len++)
{
vector<vector> dp;
dp.assign(len + 1, vector(len + 1, -1));
for (int r1 = 0; r1 <= min(len,m_c-1); r1++)
{
for (int r2 = 0; r2 <= r1; r2++)
{
const int c1 = len - r1;
const int c2 = len - r2;
if ((c1 >= m_c) || (c2 >= m_c))
{
continue;
}
if ((grid[r1][c1] < 0) || (grid[r2][c2] < 0))
{
continue;
}
int tmp1 = grid[r1][c1];
if (r1 != r2)
{
tmp1 += grid[r2][c2];
}
int tmp2 = -1;
Test(tmp2, grid, r1, r2, len - 1);
Test(tmp2, grid, r1 - 1, r2, len - 1);
Test(tmp2, grid, r1, r2 - 1, len - 1);
Test(tmp2, grid, r1 - 1, r2 - 1, len - 1);
if (tmp2 < 0)
{
continue;
}
dp[r1][r2] = tmp1 + tmp2;
}
}
m_preDp.swap(dp);
}
return max(0,m_preDp[m_c-1][m_c-1]);
}
void Test(int& iNew,const vector<vector>& grid,int r1, int r2, int iPreLen)
{
if (r1 < 0 || r1 > iPreLen)
{
return;
}
if (r2 < 0 || r2 > iPreLen)
{
return;
}
if (r1 < r2)
{
int tmp = r1;
r1 = r2;
r2 = tmp;
}
if (m_preDp[r1][r2] < 0)
{
return;
}
iNew = max(iNew,m_preDp[r1][r2]);
}
int m_c;
vector<vector> m_preDp;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

这篇关于【动态规划】【C++算法】741摘樱桃的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进