三维动态规划-LeetCode3418. 机器人可以获得的最大金币数

server/2025/4/2 5:46:05/

太爽了!做完这道题,让我感觉就像是斩杀了一条大龙!历时72天,分3次花掉30小时。终获突破!

零、题目

3418. 机器人可以获得的最大金币数

给你一个 m x n 的网格。一个机器人从网格的左上角 (0, 0) 出发,目标是到达网格的右下角 (m - 1, n - 1)。在任意时刻,机器人只能向右或向下移动。

网格中的每个单元格包含一个值 coins[i][j]

  • 如果 coins[i][j] >= 0,机器人可以获得该单元格的金币。
  • 如果 coins[i][j] < 0,机器人会遇到一个强盗,强盗会抢走该单元格数值的 绝对值 的金币。

机器人有一项特殊能力,可以在行程中 最多感化 2个单元格的强盗,从而防止这些单元格的金币被抢走。

注意:机器人的总金币数可以是负数。

返回机器人在路径上可以获得的 最大金币数 

一、思路诞生

最开始没有思路。对于动态规划做了很多题。

动态规划现在总结为五大步:

0.从(0,0)之类的挪到(1,1)方便使用

1.确定能影响结果的自变量

2.定义一个dp,当然可以是多元函数。C语言支持多维数组。

给出dp[i][j]的含义。用中文

3.写出状态转移方程。根据最后一个,通常是最后一个,或者右下角

4.求解结果:取出最后一个(或者最后一叠)中的最值。

二、本题思路

由于读完题后,发现这里有几个关键的自变量。

一个是二维数组的坐标(m,n)

另一个是使用感化的次数k

由于金币相当于是已知信息,并且这个信息不受其他信息的影响是绝对不变的。

而要求解问题时,某个具体坐标的具体感化次数都是会受到影响的。

倘若没有感化次数,也就是每资格感化,绝对不变,也就是不受其他信息的影响,那就没必要使用动态规划函数里面的一个变量。

于是就开始写状态转移方程

dp(m,n,k)表示从(1,1)到(m,n)恰好使用k次感化所能获得的最大金币数。(到4.得到结果那里发现这个定义不够准确,但是状态转移方程那里倒是对了)

当然也可以用别的方式定义,但是状态方程会变化。这种我自己觉得比价好想。

而后就是从k=0开始考量,一点一点考量。k=1开始考量。一点一点的。

最后想出了状态转移方程的全部(见草稿纸)

然后中间有一点卡住了,就是一直在想这一格是感化还是不感化呢。后来看了题干,恍然大悟,大于等于0的时候,根本不能感悟。只有小于0的时候才能感悟。于是,就应该根据这个格子金币是否小于0做出判断。

 

三、开始写代码

分为四部分:数据读入并迁移、数据初始化、状态转移、得到结果

1.数据读入并迁移

力扣里面int maximumAmount(int** coins, int coinsSize, int* coinsColSize)的含义就是

数组,行长度(有多少行),列长度(有多少列)

int extGrid[600][600];int i, j, k;int m = coinsSize; int n = *coinsColSize;for (i = 0; i < m; i++) {for (j = 0; j < n; j++) {extGrid[i + 1][j + 1] = coins[i][j];//printf("%d ",coins[i][j]);}}for (i = 1; i <= m; i++) {for (j = 1; j <= n; j++) {printf("%d ", extGrid[i][j]);}puts("");}

这样就可以完成迁移并打印检查了。

2.数据初始化

数据初始化就是考虑第0行和第0列。这是一个常规操作。但是由于这里金币范围是[-1000,1000]设置一个负无穷什么的就可以。但是我这里做了一小点创新。就是我使用了一种自定义的最大值获取,我称为Emax,存在最大值existMax。因为状态转移方程里面,会看到如果这种情况不存在的话,那就不用考虑。而最后放入dp格子中时,肯定要加入extGrid[i][j],那么这些不存在的情况其实就是0.所以来看一下Emax的实现

int isAlive(int mem[3]) {int r = mem[0];int q = mem[1];if (r == 0) {return 0;}if (q == 0) {return 0;}return 1;
}
int getNum(int mem[3]) {int r = mem[0];int q = mem[1];int s = mem[2];return dp[r][q][s];
}
int Emax(int mem1[3], int mem2[3]) {int ali1 = isAlive(mem1);int ali2 = isAlive(mem2);if (!ali1&&!ali2) {return 0;//都不存在,该值为0}if (!ali1 && ali2) {return getNum(mem2);}if (ali1 && !ali2) {return getNum(mem1);}//都存在,返回较大的return fmax(getNum(mem1), getNum(mem2));
}

3.状态转移

这一步的关键就是准确列出所有情况。分类讨论。

这里有两点创新。

一个是因为每次写dp[i][j][k]挺累的,不妨弄一个member成员,用来方便书写。每次只要传member就可以了。另外就是可以看到先用Emax取存在的结果,再用Emax得到的结果使用库函数fmax取最大值得到结果。

for (i = 1; i <= m; i++) {for (j = 1; j <= n; j++) {if (extGrid[i][j] >= 0) {for (k = 0; k <= 2; k++) {int mem1[3] = { i - 1,j,k };int mem2[3] = { i,j - 1,k };dp[i][j][k] = Emax(mem1, mem2)+extGrid[i][j];}}else {//dp(m,n,0)k = 0;int mem1[3] = { i - 1,j,k };int mem2[3] = { i,j - 1,k };dp[i][j][k] = Emax(mem1, mem2) + extGrid[i][j];//dp(m,n,1)k = 1;int bmem1[3] = { i - 1,j,k };int bmem2[3] = { i,j - 1,k };k = 0;int bmem3[3] = { i - 1,j,k };int bmem4[3] = { i,j - 1,k };int t1= Emax(bmem1, bmem2) + extGrid[i][j];int t2 = Emax(bmem3, bmem4);k = 1;dp[i][j][k] = fmax(t1,t2) ;//dp(m,n,2)k = 2;int cmem1[3] = { i - 1,j,k };int cmem2[3] = { i,j - 1,k };k = 1;int cmem3[3] = { i - 1,j,k };int cmem4[3] = { i,j - 1,k };t1 = Emax(cmem1, cmem2) + extGrid[i][j];t2 = Emax(cmem3, cmem4);k = 2;dp[i][j][k] = fmax(t1, t2);}}}

4.得到结果

这里就只需要取最后的位置,摘取胜利果实就可以了。

return fmax(fmax(dp[m][n][0], dp[m][n][1]), dp[m][n][2]);

不过,需要注意的是,感化次数为2,自然一定比恰使用感化次数为1的金币数多。因为多一次机会。不过是否每次都能有2次感化的机会呢?需要得有2个负数才可以。否则2次感化就是抄的之前的结果。如果之前一直为0的话。那就出故障了。所以还是应该fmax最好。不过巧了。这里没有影响。因为实际上我们实现的代码dp的含义是,感化次数最多为k的时候,所能得到的最大金币数量。这样状态转移方程才合理。

四、提交改BUG

0.本地vs2022调试失败

出现栈溢出错误。上网学习,发现改一下就好了。

dp[600][600][3]这是数组,肯定是在栈上的。

如果用malloc,就会在堆上,但是会很麻烦。

感谢下面的博客教我计算。格子里的数字单位是字节。差不多10MB的内存能跑起来,100MB或者200MB肯定就够用了

VS运行时报错:未经处理的异常-CSDN博客​​​​​​​

修好啦!

1.小于等于号漏等于

分类讨论那里。就需要注意不能只是小于。只小于最后的结果,就是输出的k=0,k=1全部都是0.用调试,调了一下感觉没那么方便但是有一些一调试就是一下就能看出来。其实还是看调试那里,感觉监视窗口里面i,j,k的值不变,就哪里有问题哪里打断点,比较快地解决了。

2.在372个测试样例时出现了超时

一交,对了300多个。我很激动。说明算法对了。哪里还需要小小优化一下。

一种是算法不是最快的,但是我这O(N^2)已经最快了。不是这个原因

另一种是差个系数。我觉得是这个。

①首先调整了isAlive代码尝试变快

收效甚微

②其次问chatGLM智谱清言

告诉我了这个。我觉得ptintf这个I/O输出很有道理(下图第4条),就试了一下。

结果没想到从790ms一下子变成了27ms,快了将近30倍。我的妈呀。

这个一改。就过了。

五、完整代码

通关截图:

#include <stdio.h>
#include <malloc.h>
#include <math.h>
int dp[600][600][3];//0,1,2表示感化次数
int isAlive(int mem[3]) {int r = mem[0];int q = mem[1];if (r == 0) {return 0;}if (q == 0) {return 0;}return 1;
}
int getNum(int mem[3]) {int r = mem[0];int q = mem[1];int s = mem[2];return dp[r][q][s];
}
int Emax(int mem1[3], int mem2[3]) {int ali1 = isAlive(mem1);int ali2 = isAlive(mem2);if (!ali1&&!ali2) {return 0;//都不存在,该值为0}if (!ali1 && ali2) {return getNum(mem2);}if (ali1 && !ali2) {return getNum(mem1);}//都存在,返回较大的return fmax(getNum(mem1), getNum(mem2));}
int maximumAmount(int** coins, int coinsSize, int* coinsColSize) {int extGrid[600][600];int i, j, k;int m = coinsSize; int n = *coinsColSize;for (i = 0; i < m; i++) {for (j = 0; j < n; j++) {extGrid[i + 1][j + 1] = coins[i][j];//printf("%d ",coins[i][j]);}}for (i = 1; i <= m; i++) {for (j = 1; j <= n; j++) {printf("%d ", extGrid[i][j]);}puts("");}for (i = 1; i <= m; i++) {for (j = 1; j <= n; j++) {if (extGrid[i][j] >= 0) {for (k = 0; k <= 2; k++) {int mem1[3] = { i - 1,j,k };int mem2[3] = { i,j - 1,k };dp[i][j][k] = Emax(mem1, mem2)+extGrid[i][j];}}else {//dp(m,n,0)k = 0;int mem1[3] = { i - 1,j,k };int mem2[3] = { i,j - 1,k };dp[i][j][k] = Emax(mem1, mem2) + extGrid[i][j];//dp(m,n,1)k = 1;int bmem1[3] = { i - 1,j,k };int bmem2[3] = { i,j - 1,k };k = 0;int bmem3[3] = { i - 1,j,k };int bmem4[3] = { i,j - 1,k };int t1= Emax(bmem1, bmem2) + extGrid[i][j];int t2 = Emax(bmem3, bmem4);k = 1;dp[i][j][k] = fmax(t1,t2) ;//dp(m,n,2)k = 2;int cmem1[3] = { i - 1,j,k };int cmem2[3] = { i,j - 1,k };k = 1;int cmem3[3] = { i - 1,j,k };int cmem4[3] = { i,j - 1,k };t1 = Emax(cmem1, cmem2) + extGrid[i][j];t2 = Emax(cmem3, cmem4);k = 2;dp[i][j][k] = fmax(t1, t2);}}}for (k = 0; k <= 2; k++) {printf("k is %d\n", k);for (i = 1; i <= m; i++) {for (j = 1; j <= n; j++) {//printf("dp[%d][%d][%d]=%d\n", i, j, k, dp[i][j][k]);printf("%d ", dp[i][j][k]);}puts("");}}return fmax(fmax(dp[m][n][2], dp[m][n][1]), dp[m][n][2]);
}
int main() {int* arr1 = (int*)malloc(sizeof(int) * 3);int *arr2= (int*)malloc(sizeof(int) * 3);int* arr3 = (int*)malloc(sizeof(int) * 3);int coins2 [3][3] = {{0, 1, -1},{1, -2, 3},{2, -3, 4}};int** coins = (int**)malloc(sizeof(int*) * 3);coins[0] = arr1; coins[1] = arr2; coins[2] = arr3;int i, j;for (i = 0; i < 3; i++) {for (j = 0; j < 3; j++) {coins[i][j] = coins2[i][j];}}int col = 3;int ans = maximumAmount(coins,3,&col);printf("\nans is %d\n", ans);return 0;
}

补负一、失败的思路

因为代码太复杂,写初始化卡死了。初始化想不通。

失败的代码。从失败中获得了启发。想到了Emax函数
typedef struct Square{int a0;int a1;int a2;int negativeMostMax;int negativeSecondMax;int money;
}sq;
int fmaxnum(int a, int b){if(a==b){return 3;}if(a>b){return 1;}return 2;
}
void changeNegative(sq lastSq,sq * square){int min1=lastSq.negativeMostMax;int min2=lastSq.negativeSecondMax;int m=square->money;if(m<min1){min2=min1;min1=m;}else if(m<min2){min2=m;}square->negativeMostMax=min1;square->negativeSecondMax=min2;
}
void showSquareArr(sq sqArr[600][600],int m,int n){puts("");puts("k=0");int i,j;for(i=0;i<=m;i++){for(j=0;j<=n;j++){printf("%d ",sqArr[i][j].a0);}puts("");}puts("");puts("");puts("negativeMostMax");for(i=0;i<=m;i++){for(j=0;j<=n;j++){printf("%d ",sqArr[i][j].negativeMostMax);}puts("");}puts("");puts("");puts("negativeSecondMax");for(i=0;i<=m;i++){for(j=0;j<=n;j++){printf("%d ",sqArr[i][j].negativeSecondMax);}puts("");}puts("");// puts("");// puts("k=1");// for(i=0;i<=m;i++){//     for(j=0;j<=n;j++){//         printf("%d ",sqArr[i][j].a1);//     }//     puts("");// }// puts("");// puts("");// puts("k=2");// for(i=0;i<=m;i++){//     for(j=0;j<=n;j++){//         printf("%d ",sqArr[i][j].a2);//     }//     puts("");// }// puts("");// puts("");// puts("moneyGrid");// for(i=0;i<=m;i++){//     for(j=0;j<=n;j++){//         printf("%d ",sqArr[i][j].money);//     }//     puts("");// }// puts("");}int maximumAmount(int** coins, int coinsSize, int* coinsColSize) {int i,j;int i_,j_;sq extGrid[600][600]={9};//11:10开始思考//11:20有思路,开始验证//11:20-11:40更新思路,开始码代码//13;13 了解leetcode规则//1413开始码字int m=coinsSize;int n=*coinsColSize;//showSquareArr(extGrid,m,n);//矩阵拓展.存储moneyfor(i=0,i_=1;i<m;i++,i_++){for(j=0,j_=1;j<n;j++,j_++){extGrid[i_][j_].money=coins[i][j];}}//showSquareArr(extGrid,m,n);//进行矩阵初始化。(k=0)extGrid[1][1].a0=extGrid[1][1].money;//初始化第一列j=1;for(i=2;i<=m;i++){extGrid[i][j].a0=extGrid[i-1][j].a0+extGrid[i][j].money;}//初始化第一行i=1;for(j=2;j<=n;j++){extGrid[i][j].a0=extGrid[i][j-1].a0+extGrid[i][j].money;}//状态转移for(i=2;i<=m;i++){for(j=2;j<=n;j++){extGrid[i][j].a0=fmax(extGrid[i][j-1].a0,extGrid[i-1][j].a0)+extGrid[i][j].money;}}//进行矩阵初始化。(最小值的建立)extGrid[1][1].negativeMostMax=extGrid[1][1].money;//注意第一行第一列只有一个有效值。第一行第二列有2个有效值int mt;mt=extGrid[1][2].money;if(mt<extGrid[1][1].negativeMostMax){extGrid[1][2].negativeSecondMax=extGrid[1][1].negativeMostMax;extGrid[1][2].negativeMostMax=mt;}else{extGrid[1][2].negativeMostMax=extGrid[1][1].negativeMostMax;extGrid[1][2].negativeSecondMax=mt;}//初始化第一行.j从3开始i=1;for(j=3;j<=n;j++){changeNegative(extGrid[i][j-1],&extGrid[i][j]);}//注意第一行第一列只有一个有效值。第二行第一列有2个有效值mt=extGrid[2][1].money;if(mt<extGrid[1][1].negativeMostMax){extGrid[2][1].negativeSecondMax=extGrid[1][1].negativeMostMax;extGrid[2][1].negativeMostMax=mt;}else{extGrid[2][1].negativeMostMax=extGrid[1][1].negativeMostMax;extGrid[2][1].negativeSecondMax=mt;}//初始化第一列.i从3开始j=1;for(i=3;i<=m;i++){changeNegative(extGrid[i-1][j],&extGrid[i][j]);}//状态转移for(i=2;i<=m;i++){for(j=2;j<=n;j++){int num=fmaxnum(extGrid[i][j-1].a0,extGrid[i-1][j].a0);extGrid[i][j].a0=fmax(extGrid[i][j-1].a0,extGrid[i-1][j].a0)+extGrid[i][j].money;}}showSquareArr(extGrid,m,n);return 0;
}

 

感谢大家的观看和喜欢!祝读者朋友们也屠龙痛快!

 

 

 

 


http://www.ppmy.cn/server/179819.html

相关文章

Day16 -实例:Web利用邮箱被动绕过CDN拿真实ip

本想测试一下全局ping&#xff0c;刚好注册的时候收到了邮件&#xff0c;刚好去做一下复现。 原理&#xff1a;主动让对方站点给我们发邮件&#xff08;注册、修改密码、订阅推送等&#xff09;我们查看邮件原文&#xff0c;原文里存在真实的邮件站点ip 特点&#xff1a;邮件…

[python]基于yolov10实现热力图可视化支持图像视频和摄像头检测

YOLOv10 Grad-CAM 可视化工具 本工具基于YOLOv10模型&#xff0c;结合Grad-CAM技术实现目标检测的可视化分析&#xff0c;支持图像、视频和实时摄像头处理。 功能特性 支持多种Grad-CAM方法实时摄像头处理视频文件处理图像文件处理 环境要求 Python 3.8需要电脑带有nvidia…

复杂的数据类型

简单的数据类型如&#xff1a;float、int、double、char复杂的数据类型如&#xff1a;数组、指针、结构 一、数组 &#xff08;1&#xff09;数组把多个同一类型的值存储在同一个变量名下。 &#xff08;2&#xff09;定义数组&#xff1a;类型 数组名[数组长度] 如&…

科软25机试

题目: 2025科软复试上机题&#xff08;回忆版&#xff09;题解_哔哩哔哩_bilibili 1. 字符串反转 #include<bits/stdc.h> using namespace std;void solve(string& a, int CurN) {if (!(CurN % 2)) {int right a.size() - 1;int left 0;while (left < right)…

EF Core 执行原生SQL语句

文章目录 前言一、执行查询&#xff08;返回数据&#xff09;1&#xff09; 使用 FromSqlRaw或 FromSqlInterpolated 方法&#xff0c;适用于 DbSet<T>&#xff0c;返回实体集合。2&#xff09;结合 LINQ 查询3&#xff09;执行任意原生SQL查询语句&#xff08;使用ADO.N…

Eclipse IDE for ModusToolbox™ 3.4环境通过JLINK调试CYT4BB

使用JLINK在Eclipse IDE for ModusToolbox™ 3.4环境下调试CYT4BB&#xff0c;配置是难点。总结一下在IDE中配置JLINK调试中遇到的坑&#xff0c;以及如何一步一步解决遇到的问题。 1. JFLASH能够正常下载程序 首先要保证通过JFLASH(我使用的J-Flash V7.88c版本)能够通过JLIN…

【Flask公网部署】采用Nginx+gunicorn解决Flask框架静态资源无法加载的问题

解决Flask框架静态资源无法加载的问题 1.【解决的问题】2. Flask应用的完整示例&#xff0c;包含背景图、CSS和JS的静态文件部署&#xff1a;2.1 项目结构&#xff1a;2.2 app.py 内容&#xff1a;2.3 templates/index.html 内容&#xff1a;2.4 static/css/style.css 内容&…

使用LLaMAFactory微调Qwen大模型

一、环境配置与工具安装 1. 硬件要求 GPU:至少1块NVIDIA GPU(推荐RTX 4090/A100/H100,显存≥16GB)。内存:≥64GB系统内存。存储:≥100GB硬盘空间用于模型与数据集存储。2. 软件依赖 Python 3.8+:需安装CUDA支持的PyTorch版本(如torch==2.0.1+cu117)。 依赖库:通过以…