【状态机DP】【记忆化搜索1:1翻译递归空间优化】力扣2771. 构造最长非递减子数组

news2024/10/24 8:17:02

给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,长度均为 n 。

让我们定义另一个下标从 0 开始、长度为 n 的整数数组,nums3 。对于范围 [0, n - 1] 的每个下标 i ,你可以将 nums1[i] 或 nums2[i] 的值赋给 nums3[i] 。

你的任务是使用最优策略为 nums3 赋值,以最大化 nums3 中 最长非递减子数组 的长度。

以整数形式表示并返回 nums3 中 最长非递减 子数组的长度。

注意:子数组 是数组中的一个连续非空元素序列。

示例 1:
输入:nums1 = [2,3,1], nums2 = [1,2,1]
输出:2
解释:构造 nums3 的方法之一是:
nums3 = [nums1[0], nums2[1], nums2[2]] => [2,2,1]
从下标 0 开始到下标 1 结束,形成了一个长度为 2 的非递减子数组 [2,2] 。
可以证明 2 是可达到的最大长度。

示例 2:
输入:nums1 = [1,3,2,1], nums2 = [2,2,3,4]
输出:4
解释:构造 nums3 的方法之一是:
nums3 = [nums1[0], nums2[1], nums2[2], nums2[3]] => [1,2,3,4]
整个数组形成了一个长度为 4 的非递减子数组,并且是可达到的最大长度。

示例 3:
输入:nums1 = [1,1], nums2 = [2,2]
输出:2
解释:构造 nums3 的方法之一是:
nums3 = [nums1[0], nums1[1]] => [1,1]
整个数组形成了一个长度为 2 的非递减子数组,并且是可达到的最大长度。
在这里插入图片描述

记忆化搜索

class Solution {
public:
    int maxNonDecreasingLength(vector<int>& nums1, vector<int>& nums2) {
        //记忆化搜索
        int n = nums1.size();
        int memo[n][2];
        memset(memo, -1, sizeof(memo));
        vector<vector<int>> nums(2, vector<int>(n));
        nums[0] = {nums1.begin(), nums1.end()};
        nums[1] = {nums2.begin(), nums2.end()};

        function<int(int, int)> dfs = [&](int i, int j) -> int{
            if(i == 0) return 1;
            if(memo[i][j] != -1) return memo[i][j];
            memo[i][j] = 1;
            if(nums1[i-1] <= nums[j][i]) memo[i][j] = dfs(i-1, 0) + 1;
            if(nums2[i-1] <= nums[j][i]) memo[i][j] = max(memo[i][j], dfs(i-1, 1) + 1);
            return memo[i][j];
        };

        int mx = 0;
        for(int i = 0; i < n; i++){
            for(int j = 0; j < 2; j++){
                mx = max(mx, dfs(i, j));
            }
        }
        return mx;
    }
};

我们定义dfs(i, j)为以nums[j][i]结尾的最长非递减子序列长度。
为了在记忆化搜索里面更好地进行递归,我们定义nums[0][i]为nums1,nums[1][j]为nums[2]。

在记忆化搜索的递归函数中,我们定义当i = 0的时候,记忆化搜索终止,返回1。并且我们通过memo[i][j]来记录已经计算过的值,如果memo[i][j]不为-1,说明之前计算过,直接返回即可。

然后如果这时候i既不是0,并且没有计算过dfs(i, j)的值,那么我们就令memo[i][j]先为1,这样即使后面两个if都不通过,也能返回一个正确的值。

然后我们这时候进行判断,看我们现在nums[j][i]与nums1[i-1]和nums2[i-1]进行比较,如果大于等于他们,则说明我们这时候的最大非递减子序列长度就是要么dfs(i-1, 0)+1,要么就是dfs(i-1, 1)+1,取两者最大值即可。

在最后,我们遍历每个nums1和nums2数组的元素,将它作为最长非递减子序列的末尾元素,然后用变量mx来记录最大长度。

翻译成递推

class Solution {
public:
        int maxNonDecreasingLength(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size(),f[n][2];
        f[0][0] = f[0][1] = 1;
        vector<vector<int>> nums(2,vector<int>(n));
        nums[0] = {nums1.begin(),nums1.end()};
        nums[1] = {nums2.begin(),nums2.end()};
        int mx = 1;
        for(int i = 1;i < n; i++){
            for(int j = 0;j < 2 ;j++){
                f[i][j] = 1;
                if(nums1[i - 1] <= nums[j][i]) f[i][j] = f[i - 1][0] + 1;
                if(nums2[i - 1] <= nums[j][i]) f[i][j] = max(f[i][j],f[i - 1][1] + 1);
                mx = max(mx,f[i][j]);
            }

        }
        return mx;
    }
};

我们也可以将记忆化搜索1:1翻译成递推的形式。

空间优化

class Solution {
public:
        int maxNonDecreasingLength(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size(),f[2];
        f[0] = f[1] = 1;
        vector<vector<int>> nums(2,vector<int>(n));
        nums[0] = {nums1.begin(),nums1.end()};
        nums[1] = {nums2.begin(),nums2.end()};
        int mx = 1;
        for(int i = 1;i < n; i++){
            int t0 = f[0], t1 = f[1];
            for(int j = 0;j < 2 ;j++){
                f[j] = 1;
                if(nums1[i - 1] <= nums[j][i]) f[j] = t0 + 1;
                if(nums2[i - 1] <= nums[j][i]) f[j] = max(f[j],t1 + 1);
                mx = max(mx,f[j]);
            }

        }
        return mx;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)

我们可以对递推代码进行空间优化,在这里f[i][j] = f[i - 1][0] + 1以及f[i][j] = max(f[i][j],f[i - 1][1] + 1);,f[i][j]都是由上一轮i-1转换而来,由于我们在使用一位数组的时候,会存在覆盖,使f[j]变成这一轮计算过的值而不是上一轮,所以我们在每一轮开始的时候,通过两个变量t0和t1来记录上一轮的f[0]和f[1]供着一轮的状态转移使用。

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

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

相关文章

LabVIEW提高开发效率技巧----离线调试

离线调试是LabVIEW开发中一项重要的技巧&#xff0c;通过使用Simulate Signal Express VI生成虚拟数据&#xff0c;开发者能够有效减少对实际硬件的依赖&#xff0c;加速开发过程。这种方法不仅可以提高开发效率&#xff0c;还能降低成本&#xff0c;增强系统的灵活性。 ​ 离…

网安加·百家讲坛 | 徐一丁:金融机构网络安全合规浅析

作者简介&#xff1a;徐一丁&#xff0c;北京小西牛等保软件有限公司解决方案部总监&#xff0c;网络安全高级顾问。2000年开始从事网络安全工作&#xff0c;主要领域为网络安全法规标准研究、金融行业安全咨询与解决方案设计、信息科技风险管理评估等。对国家网络安全法规标准…

深度学习 之 模型部署 使用Flask和PyTorch构建图像分类Web服务

引言 随着深度学习的发展&#xff0c;图像分类已成为一项基础的技术&#xff0c;被广泛应用于各种场景之中。本文将介绍如何使用Flask框架和PyTorch库来构建一个简单的图像分类Web服务。通过这个服务&#xff0c;用户可以通过HTTP POST请求上传花朵图片&#xff0c;然后由后端…

【Qt】QTableView添加下拉框过滤条件

实现通过带复选框的下拉框来为表格添加过滤条件 带复选框的下拉框 .h文件 #pragma once #include <QCheckBox> #include <QComboBox> #include <QEvent> #include <QLineEdit> #include <QListWidget>class TableComboBox : public QComboBox …

前端构建工具vite的优势

1. 极速冷启动 Vite 使用原生 ES 模块 (ESM) 在开发环境下进行工作。相比于传统构建工具需要打包所有的文件&#xff0c;Vite 只在浏览器请求模块时动态加载所需的文件。无打包冷启动&#xff1a;无需预先打包&#xff0c;项目启动非常快&#xff0c;尤其对于大型项目效果更明…

顺序表(一)(数据结构)

一. 线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列 。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;是人为想象出来的数…

HCIP--1

同一区域内的OSPF路由器拥有一致的 LSDB, 在区域内&#xff0c;OSPF 采用 SPF算法计算路由一个区域太多路由器&#xff0c;硬件资源跟不上&#xff0c;所以多划分区域 OSPF 路由计算原理 1. 区域内路由计算 LSA 在OSPF中&#xff0c;每个路由器生成 LSA&#xff0c;用于告诉…

【部署篇】RabbitMq-03集群模式部署

一、准备主机 准备3台主机用于rabbitmq部署&#xff0c;文章中是在centos7上安装部署rabbitmq3.8通过文章中介绍的方式可以同样在centos8、centos9上部署&#xff0c;只需下载对应的版本进行相同的操作。 主机IP角色说明192.168.128.31种子节点192.168.128.32普通节点192.16…

Matlab学习01-矩阵

目录 一&#xff0c;矩阵的创建 1&#xff0c;直接输入法创建矩阵 2&#xff0c;利用M文件创建矩阵 3&#xff0c;利用其它文本编辑器创建矩阵 二&#xff0c;矩阵的拼接 1&#xff0c;基本拼接 1&#xff09; 水平方向的拼接 2&#xff09;垂直方向的拼接 3&#xf…

Linux系统基础-进程间通信(5)_模拟实现命名管道和共享内存

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Linux系统基础-进程间通信(5)_模拟实现命名管道和共享内存 收录于专栏[Linux学习] 本专栏旨在分享学习Linux的一点学习笔记&#xff0c;欢迎大家在评论区交流讨…

LeetCode--删除并获得点数--动态规划

一、题目解析 二、算法原理 根据题意&#xff0c;在选择了元素 x 后&#xff0c;该元素以及所有等于 x−1 或 x1 的元素会从数组中删去。若还有多个值为 x 的元素&#xff0c;由于所有等于 x−1 或 x1 的元素已经被删除&#xff0c;我们可以直接删除 x 并获得其点数。因此若选…

Win10+MinGW13.1.0编译Qt5.15.15

安装windows SDK、python、ruby、cmake、Perl[可选]安装MySQL解压qt-everywhere-opensource-src-5.15.15.zip&#xff08;注&#xff1a;不要使用qt-everywhere-opensource-src-5.15.15.tar.xz&#xff09;修改源代码 E:\qt-everywhere-src-5.15.15\qtbase\src\3rdparty\angle\…

hive数据库,表操作

1.创建; create database if not exists myhive; use myhive; 2.查看: 查看数据库详细信息:desc database myhive; 默认数据库的存放路径是 HDFS 的&#xff1a; /user/hive/warehouse 内 补充:创建数据库并指定 hdfs 存储位置:create database myhive2 location /myhive2 3.…

<项目代码>YOLOv8路面垃圾识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

Codeforces Round 881 (Div. 3)(A~F1题解)

这场比赛可能是因为比较老吧&#xff0c;我感觉很轻松&#xff0c;就是第五个卡了一下&#xff0c;看错题了&#xff0c;原本应该是严格大于的&#xff0c;从而导致代码一直出现bug&#xff0c;在1小时20分钟才解决 A. Sasha and Array Coloring 题意&#xff1a;就是说给你n个…

提权 | Windows系统

文章目录 cmd提权meterpreter提权getsystemsteal_tokenmigrate 令牌窃取(MS16-075)烂土豆提权步骤烂土豆提权原理 sc命令提权抓本地密码提权其他工具pr工具 内核提权WindowsVulScan cmd提权 前言&#xff1a;我们getshell一个用windows部署的网站后&#xff0c;通过蚁剑或者其…

ESP32 S3 语音识别 语音唤醒程序流程

ESP32 S3 语音识别 语音唤醒程序流程 参考例程首先进行esp_periph_set_init 初始化之后执行setup_player&#xff0c;之后执行start_recorder&#xff0c;识别的主处理voice_read_task 参考例程 D:\Espressif\esp-adf\examples\speech_recognition\wwe\ 首先进行esp_periph_se…

零知识学习WLAN漫游二、无线漫游介绍(2)

接前一篇文章&#xff1a;零知识学习WLAN漫游一、无线漫游介绍&#xff08;1&#xff09; 本文内容参考&#xff1a; WLAN漫游简介_漫游主动性-CSDN博客 无线漫游_百度百科 无线漫游简述-CSDN博客 特此致谢&#xff01; 一、WLAN漫游简介 3. 漫游协议和快速漫游协议 802.…

算法的学习笔记—数字在排序数组中出现的次数(牛客JZ53)

&#x1f600;前言 在编程中&#xff0c;查找有序数组中特定元素的出现次数是一个常见的问题。本文将详细讲解这个问题的解决方案&#xff0c;并通过二分查找法优化效率。 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 &#x1f970;数字在排序数组中出现的次数&#x…

九、pico+Unity交互开发——触碰抓取

一、VR交互的类型 Hover&#xff08;悬停&#xff09; 定义&#xff1a;发起交互的对象停留在可交互对象的交互区域。例如&#xff0c;当手触摸到物品表面&#xff08;可交互区域&#xff09;时&#xff0c;视为触发了Hover。 Grab&#xff08;抓取&#xff09; 概念&#xff…