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#如何动态创建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

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

一文详解SpringBoot响应压缩功能的配置与优化

《一文详解SpringBoot响应压缩功能的配置与优化》SpringBoot的响应压缩功能基于智能协商机制,需同时满足很多条件,本文主要为大家详细介绍了SpringBoot响应压缩功能的配置与优化,需... 目录一、核心工作机制1.1 自动协商触发条件1.2 压缩处理流程二、配置方案详解2.1 基础YAML

MySQL中慢SQL优化的不同方式介绍

《MySQL中慢SQL优化的不同方式介绍》慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化,下面小编就来给大家介绍一下有哪些方式可以优化慢SQL吧... 目录避免不必要的列分页优化索引优化JOIN 的优化排序优化UNION 优化慢 SQL 的优化,主要从两个方面考虑,SQL 语

MySQL中慢SQL优化方法的完整指南

《MySQL中慢SQL优化方法的完整指南》当数据库响应时间超过500ms时,系统将面临三大灾难链式反应,所以本文将为大家介绍一下MySQL中慢SQL优化的常用方法,有需要的小伙伴可以了解下... 目录一、慢SQL的致命影响二、精准定位问题SQL1. 启用慢查询日志2. 诊断黄金三件套三、六大核心优化方案方案

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

查看Oracle数据库中UNDO表空间的使用情况(最新推荐)

《查看Oracle数据库中UNDO表空间的使用情况(最新推荐)》Oracle数据库中查看UNDO表空间使用情况的4种方法:DBA_TABLESPACES和DBA_DATA_FILES提供基本信息,V$... 目录1. 通过 DBjavascriptA_TABLESPACES 和 DBA_DATA_FILES