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

news2024/11/28 19:03:54

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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1168579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

匪夷所思,spring aop这么写竟然会失效!!

背景 spring 版本&#xff1a;3.2.8.RELEASEJDK版本&#xff1a;1.8本地是正常&#xff0c;线上环境是有问题的 应用从云下迁移到云上的过程中出现了一个应用部分aop 通知失效的问题&#xff0c;场景如下&#xff1a; node1 节点上的category 是失效的&#xff0c;element是正…

ubuntu 分区 方案

ubuntu 分区 方案 自动分区啥样子的&#xff1f; 手动分区 需要怎么操作&#xff1f; 注意点是啥&#xff1f; swap分区 要和 内存大小 差不多 安装ubuntu系统时硬盘分区方案 硬盘分区概述 一块硬盘最多可以分4个主分区&#xff0c;主分区之外的成为扩展分区。硬盘可以没有…

C++--二叉搜索树初阶

前言&#xff1a;二叉搜索树是一种常用的数据结构&#xff0c;支持快速的查找、插入、删除操作&#xff0c;C中map和set的特性也是以二叉搜索树作为铺垫来实现的&#xff0c;而二叉搜索树也是一种树形结构&#xff0c;所以&#xff0c;在学习map和set之前&#xff0c;我们先来学…

学习率设置

在我们刚刚接触深度学习时&#xff0c;对学习率只有一个很基础的认知&#xff0c;当学习率过大的时候会导致模型难以收敛&#xff0c;过小的时候会收敛速度过慢&#xff0c;其实学习率是一个十分重要的参数&#xff0c;合理的学习率才能让模型收敛到最小点而非局部最优点或鞍点…

学 Java 怎么进外企?

作者&#xff1a;**苍何&#xff0c;CSDN 2023 年 实力新星&#xff0c;前大厂高级 Java 工程师&#xff0c;阿里云专家博主&#xff0c;土木转码&#xff0c;现任部门技术 leader&#xff0c;专注于互联网技术分享&#xff0c;职场经验分享。 &#x1f525;热门文章推荐&#…

HNU程序设计 练习三-控制结构

1.台球游戏 【问题描述】 在本台球游戏中&#xff0c;包含多种颜色的球&#xff0c;其中&#xff1a;红球15只各1分、黄球1只2分、绿球1只3分、咖啡球1只4分、蓝球1只5分、粉球1只6分、黑球1只7分。 球的颜色表示为&#xff1a; r-红色球 y-黄色球 g-绿色球 c-咖啡色球 b-蓝色…

闭循环低温恒温器的使用注意事项

与液氮恒温器相比&#xff0c;闭循环低温恒温器显得稍微复杂一些&#xff01;这主要表现在组成部分、体积重量、使用操作、升降温时间等方面。闭循环低温恒温器主要由冷头、氦压缩机、两根氦气连管组成&#xff0c;配套设备还有控温仪、真空泵&#xff0c;可能还有循环水冷机。…

离散数学实践(2)-编程实现关系性质的判断

*本文为博主本人校内的离散数学专业课的实践作业。由于实验步骤已经比较详细&#xff0c;故不再对该实验额外提供详解&#xff0c;本文仅提供填写的实验报告内容与代码部分&#xff0c;以供有需要的同学学习、参考。 -------------------------------------- 编程语言&#xff…

VM虚拟机逆向 --- [NCTF 2018]wcyvm 复现

文章目录 前言题目分析 前言 第四题了&#xff0c;搞定&#xff0c;算是独立完成比较多的一题&#xff0c;虽然在还原汇编的时候还是很多问题。 题目分析 代码很简单&#xff0c;就是指令很多。 opcode在unk_6021C0处&#xff0c;解密的数据在dword_6020A0处 opcode [0x08, …

谈一谈SQLite、MySQL、PostgreSQL三大数据库

每一份付出&#xff0c;必将有一份收货&#xff0c;就像这个小小的果实&#xff0c;时间到了&#xff0c;也就会开花结果… 三大数据库概述 SQLite、MySQL 和 PostgreSQL 都是流行的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;但它们在功能、适用场景和性…

【UE】从UI拖拽生成物体 —— 更改位置与定点销毁

本篇在上一篇博客&#xff08;【UE】从UI中拖拽生成物体-CSDN博客&#xff09;基础上继续增加更改生成的Actor的位置与定点销毁Actor的功能。 目录 效果 步骤 一、修改生成好的Actor位置 解决问题一&#xff1a;从UI拖出多个actor后&#xff0c;只能对第一个拖出的actor的…

传智杯-21算法赛初赛B组题目详细解法解析-AB题(C/C++、Python、Java)

🚀 欢迎来到 ACM 算法题库专栏 🚀 在ACM算法题库专栏,热情推崇算法之美,精心整理了各类比赛题目的详细解法,包括但不限于ICPC、CCPC、蓝桥杯、LeetCode周赛、传智杯等等。无论您是刚刚踏入算法领域,还是经验丰富的竞赛选手,这里都是提升技能和知识的理想之地。 ✨ 经典…

UG\NX二次开发 先设置默认颜色再创建对象

文章作者:里海 来源网站:里海NX二次开发3000例专栏 感谢粉丝订阅 感谢 qq_42461973 订阅本专栏,非常感谢。 简介 有粉丝问,可不可以先设置默认颜色再创建对象?这个是可以的,下面是例子: 效果 代码 #include "me.hpp" using namespace std;

java/springboot服务第三方接口安全签名(Signature)实现方案

前言 有的时候&#xff0c;我们需要把我们系统里的接口开放给第三方应用或企业使用&#xff0c;那第三方的系统并不在我们自己的认证授权用户体系内&#xff0c;此时&#xff0c;要如何保证我们接口的数据安全和身份识别呢&#xff1f; 在为第三方系统提供接口的时候&#xf…

筑基新一代数据底座,中国科大让智慧科研更有数

著名科学哲学家库恩在《科学革命的结构》中认为&#xff0c;范式是科研的一种理论体系&#xff0c;范式的突破会带来一系列科学革命。 如今在科研领域&#xff0c; 人工智能不断打破科研边界&#xff0c;AI for Science被视为下一个科研新范式&#xff0c;不仅为科学研究带来了…

Cesium:为地图添加指北针、缩放按钮和比例尺

作者&#xff1a;CSDN _乐多_ 网上找的很多代码用不了。本文记录了Cesium中为地图添加指北针、缩放按钮和比例尺的可用代码。 文章目录 一、代码 一、代码 const viewer new Cesium.Viewer(cesiumContainer, {// ...navigationHelpButton: false,sceneModePicker: false,sc…

校验验证码是否过期(定时刷新验证码)

需求&#xff1a; 我们在登录的时候会遇到通过接口请求验证码的操作&#xff0c;这里的验证码会有过期的时间&#xff0c;当我们验证码过期了&#xff0c;我们要进行重新刷新验证码。 我们这里根据后端返回的当前时间和过期时间判断&#xff0c;过期的时间超过了当前时间的时候…

Java面向对象 下(六)

Java面向对象 ( 下) 观看b站尚硅谷视频做的笔记 文章目录 Java面向对象 ( 下)1、 关键字&#xff1a;static1.1、static 的使用1.1.1、static 修饰属性1.1.2、 static 修饰方法1.1.3、 static 修饰代码块1.1.4、 static 修饰内部类1.1.5、类变量 vs 实例变量内存解析 1.2、 自…

关于msvcp120.dll丢失的解决方法详解,快速解决dll丢失问题

在计算机使用过程中&#xff0c;经常会遇到“msvcp120.dll丢失”的错误提示。这个错误提示通常出现在运行某些程序或游戏时&#xff0c;造成相关应用程序可能无法正常启动或运行。那么&#xff0c;究竟是什么原因导致了msvcp120.dll文件的丢失呢&#xff1f;本文将详细解析msvc…

【QT】文件读写

新建项目 加入控件 整体做一个布局 功能&#xff1a;选择文件路径&#xff0c;打开文件&#xff08;两种文件格式&#xff1a;utf-8、GBK&#xff09; #include "widget.h" #include "ui_widget.h" #include <QPushButton> #include <QFileDial…