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

相关文章

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情

MySQL不使用子查询的原因及优化案例

《MySQL不使用子查询的原因及优化案例》对于mysql,不推荐使用子查询,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,本文给大家... 目录不推荐使用子查询和JOIN的原因解决方案优化案例案例1:查询所有有库存的商品信息案例2:使用EX

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件

如何用Python绘制简易动态圣诞树

《如何用Python绘制简易动态圣诞树》这篇文章主要给大家介绍了关于如何用Python绘制简易动态圣诞树,文中讲解了如何通过编写代码来实现特定的效果,包括代码的编写技巧和效果的展示,需要的朋友可以参考... 目录代码:效果:总结 代码:import randomimport timefrom math

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

Java中JSON字符串反序列化(动态泛型)

《Java中JSON字符串反序列化(动态泛型)》文章讨论了在定时任务中使用反射调用目标对象时处理动态参数的问题,通过将方法参数存储为JSON字符串并进行反序列化,可以实现动态调用,然而,这种方式容易导... 需求:定时任务扫描,反射调用目标对象,但是,方法的传参不是固定的。方案一:将方法参数存成jsON字

C# ComboBox下拉框实现搜索方式

《C#ComboBox下拉框实现搜索方式》文章介绍了如何在加载窗口时实现一个功能,并在ComboBox下拉框中添加键盘事件以实现搜索功能,由于数据不方便公开,作者表示理解并希望得到大家的指教... 目录C# ComboBox下拉框实现搜索步骤一步骤二步骤三总结C# ComboBox下拉框实现搜索步骤一这