LCR 166.珠宝的最高价值 + 动态规划 + 记忆化搜索 + 递推 + 空间优化

本文主要是介绍LCR 166.珠宝的最高价值 + 动态规划 + 记忆化搜索 + 递推 + 空间优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

LCR 166. 珠宝的最高价值 - 力扣(LeetCode)


现有一个记作二维矩阵 frame 的珠宝架,其中 frame[i][j] 为该位置珠宝的价值。拿取珠宝的规则为:

  • 只能从架子的左上角开始拿珠宝
  • 每次可以移动到右侧或下侧的相邻位置
  • 到达珠宝架子的右下角时,停止拿取

 注意:珠宝的价值都是大于 0 的。除非这个架子上没有任何珠宝,比如 frame = [[0]]


(1)递归

定义:dfs(i,j) 表示从左上角 到 (i,j) 最大价值和

寻找子问题,把大问题变成小问题,分类讨论如何到达(i,j):

  • 若从左边过来,则 dfs(i,j) = dfs(i,j-1)+grid[i][j];
  • 若从上边过来,则 dfs(i,j) = dfs(i-1,j)+grid[i][j];

取这两个分类情况的最大值dfs(i,j) = max(dfs(i,j-1),dfs(i-1,j))+grid[i][j];

  • 递归边界:当 i < 0j < 0 时,返回 0,因为出界没有价值的,也就是        
    • dfs(-1,j)=0,dfs(i,-1)=0
  • 递归入口:
    • dfs(m-1,n-1)
class Solution {
public:// 递归 超时!!!int jewelleryValue(vector<vector<int>>& grid) {  int n=grid.size(),m=grid[0].size();function<int(int,int)> dfs = [&](int i,int j) -> int{if(i<0 || j<0) return 0;return max(dfs(i-1,j),dfs(i,j-1))+grid[i][j];};return dfs(n-1,m-1);}
};
  • 时间复杂度O(2^{n+m})
  • 空间复杂度O(n+m)

>>复杂度分析

  • 其中 n 和 分别为 grid 的行数和列数。搜索树可以近似为一棵二叉树,树高为O(n+m)。也意味着从 grid 左上角到右下角经过的格子数,所以节点个数为O(2^{n+m})
  • 递归需要 O(n+m) 的栈空间

(2)递归搜索 + 保存计算结果 = 记忆化搜索

  • 对于dfs(3,3)「先左再上」「先上再左」,都会调用 dfs(2,2)。同理,那么整个递归中有大量重复递归调用(递归入参相同)
  • 因为递归函数无副作用,同样的入参无论计算多少次,都是一样的结果,可用记忆化搜索来优化

>>注意事项

  • 如果 grid 中有 0memo 数组初始化成 -1
  • 如果 grid 中有负数memo 数组可以初始化成很大或者很小的数,如:INT_MAX,INT_MIN
class Solution {
public:// 记忆化搜索int jewelleryValue(vector<vector<int>>& grid) {  int n=grid.size(),m=grid[0].size(),memo[n][m];// vector<vector<int>> memo(n+1,vector<int>(m+1,-1));memset(memo,0,sizeof(memo));function<int(int,int)> dfs = [&](int i,int j) -> int{if(i<0 || j<0) return 0;int &res = memo[i][j];if(res) return res;// grid[i][j] 都是正数,记忆化的 memo[i][j] 必然为正数return res=max(dfs(i-1,j),dfs(i,j-1))+grid[i][j];};return dfs(n-1,m-1);}
};
class Solution {
public:// 记忆化搜索int jewelleryValue(vector<vector<int>>& grid) {  int n=grid.size(),m=grid[0].size(),memo[n+1][m+1];// vector<vector<int>> memo(n+1,vector<int>(m+1,-1));memset(memo, -1, sizeof(memo));function<int(int,int)> dfs = [&](int i,int j) -> int{if(i<0 || j<0) return 0;int &res = memo[i][j];if(res != -1) return res;return res=max(dfs(i-1,j),dfs(i,j-1))+grid[i][j];};return dfs(n-1,m-1);}
};
  • 时间复杂度O(nm)
  • 空间复杂度O(nm)

>>复杂度分析

由于每个状态只会计算一次,状态个数为O(nm),单个状态的计算时间为O(1),所以

  • 动态规划的时间复杂度 = 状态个数 x 单个状态的计算时间
  • O(nm) = O(nm) x O(1) 

(3)1:1 翻译成递推

  • 去掉递归中的「递」,只保留「归」的部分,即自底向上计算

翻译步骤:

  • dfs 改成 f 数组
  • 递归改成循环 (每个参数都对应一层循环)
  • 递归边界改成 f 数组的初始值

  • dfs(i,j) = max(dfs(i,j-1),dfs(i-1,j))+grid[i][j];

                                           

  • f[i][j] = max(f[i][j-1],f[i-1][j])+grid[i][j];

存在问题(O_O)?:当 i = 0,j = 0 时,这种定义方式没有状态能表示边界出界的情况

解决方案:在 f 数组的上边和左边各加一排,

  • f[i] -> f[i+1],f[i-1] -> f[i],f[j] -> f[j+1],f[j-1] -> f[j]
  • f[i+1][j+1] = max(f[i+1][j],f[i][j+1])+grid[i][j];

初始化:根据  dfs(i,-1) = 0dfs(-1,j) = 0 翻译f[i][0]=0,f[0][j]=0

返回最终结果:根据 dfs(m-1,n-1) 翻译 f[m][n] 


  •  ① f[i+1][j+1] = max(f[i+1][j],f[i][j+1])+grid[i][j]; (推荐)
class Solution {
public: // 递推int jewelleryValue(vector<vector<int>>& grid) {  int n=grid.size(),m=grid[0].size(),f[n+1][m+1];// vector<vector<int>> f(n+1,vector<int>(m+1,0));memset(f,0,sizeof(f));for(int i=0;i<n;i++) {for(int j=0;j<m;j++) {f[i+1][j+1] = max(f[i][j+1],f[i+1][j])+grid[i][j];}}return f[n][m];}
};
  •  ② f[i][j] = max(f[i][j-1],f[i-1][j])+grid[i][j];
class Solution {
public:// 递推int jewelleryValue(vector<vector<int>>& grid) {  int n=grid.size(),m=grid[0].size();vector<vector<int>> f(n,vector<int>(m,0));f[0][0]=grid[0][0];for(int i=0;i<n;i++) {for(int j=0;j<m;j++) {if(i==0 && j>=1) f[0][j] = f[0][j-1] + grid[0][j];if(j==0 && i>=1) f[i][0] = f[i-1][0] + grid[i][0];if(i>=1 && j>=1) f[i][j] = max(f[i-1][j],f[i][j-1])+grid[i][j];}}return f[n-1][m-1];}
};
  • 时间复杂度O(nm)
  • 空间复杂度O(nm)

(4)空间优化

  • 方法一:两个数组,滚动数组
class Solution {
public: // 递推 + 空间优化int jewelleryValue(vector<vector<int>>& grid) {  int n=grid.size(),m=grid[0].size(),f[2][m+1];// vector<vector<int>> f(2,vector<int>(m+1,0));memset(f,0,sizeof(f));for(int i=0;i<n;i++) {for(int j=0;j<m;j++) {f[(i+1)%2][j+1] = max(f[i%2][j+1],f[(i+1)%2][j])+grid[i][j];}}return f[n%2][m];}
};
  • 时间复杂度O(nm)
  • 空间复杂度O(m)

  • 方法二:一个数组,滚动数组

 本题的转移方程类似完全背包,故采用正序遍历

class Solution {
public:// 递推 + 空间优化int jewelleryValue(vector<vector<int>>& grid) {  int n=grid.size(),m=grid[0].size(),f[m+1];// vector<int>f(m+1,0);memset(f,0,sizeof(f));for(int i=0;i<n;i++) {for(int j=0;j<m;j++) {f[j+1] = max(f[j+1],f[j])+grid[i][j];}}return f[m];}
};
  • 时间复杂度O(nm)
  • 空间复杂度O(m)

  • 方法三:原地修改
class Solution {
public:// 递推 + 空间优化 + 原地修改int jewelleryValue(vector<vector<int>>& frame) {  int n=frame.size(),m=frame[0].size();for(int i=0;i<n;i++) {for(int j=0;j<m;j++) {if(i==0 && j>=1) frame[0][j] = frame[0][j-1] + frame[0][j] ;if(j==0 && i>=1) frame[i][0] = frame[i-1][0] + frame[i][0];if(i>=1 && j>=1) frame[i][j] = max(frame[i-1][j],frame[i][j-1])+frame[i][j];}}return frame[n-1][m-1];}
};
  • 时间复杂度O(nm)
  • 空间复杂度O(1)

 参考和推荐文章:

LCR 166. 珠宝的最高价值 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof/solutions/2153802/jiao-ni-yi-bu-bu-si-kao-dpcong-hui-su-da-epvl/

这篇关于LCR 166.珠宝的最高价值 + 动态规划 + 记忆化搜索 + 递推 + 空间优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

springboot使用Scheduling实现动态增删启停定时任务教程

《springboot使用Scheduling实现动态增删启停定时任务教程》:本文主要介绍springboot使用Scheduling实现动态增删启停定时任务教程,具有很好的参考价值,希望对大家有... 目录1、配置定时任务需要的线程池2、创建ScheduledFuture的包装类3、注册定时任务,增加、删

SpringBoot基于配置实现短信服务策略的动态切换

《SpringBoot基于配置实现短信服务策略的动态切换》这篇文章主要为大家详细介绍了SpringBoot在接入多个短信服务商(如阿里云、腾讯云、华为云)后,如何根据配置或环境切换使用不同的服务商,需... 目录目标功能示例配置(application.yml)配置类绑定短信发送策略接口示例:阿里云 & 腾

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分

MySQL中动态生成SQL语句去掉所有字段的空格的操作方法

《MySQL中动态生成SQL语句去掉所有字段的空格的操作方法》在数据库管理过程中,我们常常会遇到需要对表中字段进行清洗和整理的情况,本文将详细介绍如何在MySQL中动态生成SQL语句来去掉所有字段的空... 目录在mysql中动态生成SQL语句去掉所有字段的空格准备工作原理分析动态生成SQL语句在MySQL

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

C#如何动态创建Label,及动态label事件

《C#如何动态创建Label,及动态label事件》:本文主要介绍C#如何动态创建Label,及动态label事件,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#如何动态创建Label,及动态label事件第一点:switch中的生成我们的label事件接着,

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis