算法【最长递增子序列问题与扩展】
本文讲解最长递增子序列以及最长不下降子序列的最优解,以及一些扩展题目。本文中讲述的是最优解,时间复杂度是O(n*logn),空间复杂度O(n),好实现、理解难度不大。这个问题也可以用线段树来求解,时间和空间复杂度和本节讲的最优解没有区别。
下面通过一些题目来加深理解。
题目一
测试链接:https://leetcode.cn/problems/longest-increasing-subsequence/
分析:这道题的基本做法是非常简单和常见的,所以下面给出基本做法以及优化时间复杂度的做法。基本做法的时间复杂度是O(n²)。代码如下。
class Solution {
public:int dp[2500];int lengthOfLIS(vector<int>& nums) {int length = nums.size();int ans = 1;dp[0] = 1;for(int i = 1;i < length;++i){dp[i] = 1;for(int j = 0;j < i;++j){if(nums[i] > nums[j]){dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1;}}ans = ans > dp[i] ? ans : dp[i];}return ans;}
};
优化时间复杂度之后,能做到O(n*logn)。通过一个end数组,end[i]表示长度为i+1的严格递增子序列的最小结尾,故可以知道end数组是有序的,对于有序数组的查找可以采用二分查找法,这也是优化时间复杂度所在。最后end数组的长度就是最长严格递增子序列的长度。代码如下。
class Solution {
public:int end[2500];int get_index(int len, int num){int ans = -1;int l = 0, r = len - 1, m;while (l <= r){m = l + (r - l) / 2;if(end[m] >= num){ans = m;r = m - 1;}else{l = m + 1;}}return ans;}int lengthOfLIS(vector<int>& nums) {int length = nums.size();int len = 1, find;end[0] = nums[0];for(int i = 1;i < length;++i){find = get_index(len, nums[i]);if(find == -1){end[len++] = nums[i];}else{end[find] = nums[i];}}return len;}
};
题目二
测试链接:https://leetcode.cn/problems/russian-doll-envelopes/
分析:这道题的思路较为难想,其实只需要对于每个信封宽度从小到大排序,相同宽度的,对于高度从大到小排序,然后仅对高度求一个最长递增子序列即可。代码如下。
class Solution {
public:int end[100000];int get_index(int len, int num){int ans = -1;int l = 0, r = len - 1, m;while (l <= r){m = l + (r - l) / 2;if(end[m] >= num){ans = m;r = m - 1;}else{l = m + 1;}}return ans;}int maxEnvelopes(vector<vector<int>>& envelopes) {auto cmp = [](vector <int> v1, vector<int> v2){return v1[0] < v2[0] || (v1[0] == v2[0] && v1[1] > v2[1]);};sort(envelopes.begin(), envelopes.end(), cmp);int length = envelopes.size();int len = 1, find;end[0] = envelopes[0][1];for(int i = 1;i < length;++i){find = get_index(len, envelopes[i][1]);if(find == -1){end[len++] = envelopes[i][1];}else{end[find] = envelopes[i][1];}}return len;}
};
题目三
测试链接:https://leetcode.cn/problems/minimum-operations-to-make-the-array-k-increasing/
分析:这道题其实是将一个数组分为了k个子数组,然后只需要对每个子数组求一个最长不下降子序列,然后用子数组长度减去这个最长不下降子序列的长度就是需要操作的次数,将每一个子数组的操作次数求和即可得到答案。代码如下。
class Solution {
public:int nums[100000];int end[100000];int kIncreasing(vector<int>& arr, int k) {int length = arr.size();int ans = 0;int size;for(int i = 0;i < k;++i){size = 0;for(int j = i;j < length;j += k){nums[size++] = arr[j];}ans += (size - get_num(size));}return ans;}int get_num(int size){end[0] = nums[0];int len = 1;for(int i = 1, find;i < size;++i){find = get_index(nums[i], len);if(find == -1){end[len++] = nums[i];}else{end[find] = nums[i];}}return len;}int get_index(int num, int len){int ans = -1;int m;int l = 0;int r = len - 1;while (l <= r){m = (l + r) / 2;if(end[m] > num){ans = m;r = m - 1;}else{l = m + 1;}}return ans;}
};
题目四
测试链接:https://leetcode.cn/problems/maximum-length-of-pair-chain/
分析:因为结果中后一个数对的第一个数一定会比前一个数对的第一个数大,所以我们先对第一个数从小到大排序。然后对于去end数组查找的数选用数对的第一个数,但对end数组进行赋值的时候我们选用数对的第二个数,这是因为题目要求后一个数对的第一个数必须比前一个数对的第二个数大。代码如下。
class Solution {
public:int end[1000];int findLongestChain(vector<vector<int>>& pairs) {sort(pairs.begin(), pairs.end(), [](vector<int> v1, vector<int> v2)->bool{return v1[0] < v2[0];});int length = pairs.size();end[0] = pairs[0][1];int len = 1;for(int i = 0, find;i < length;++i){find = get_index(len, pairs[i][0]);if(find == -1){end[len++] = pairs[i][1];}else{end[find] = end[find] > pairs[i][1] ? pairs[i][1] : end[find];}}return len;}int get_index(int len, int num){int ans = -1;int m, l = 0, r = len - 1;while (l <= r){m = (l + r) / 2;if(end[m] >= num){ans = m;r = m - 1;}else{l = m + 1;}}return ans;}
};
这道题的最优解法是贪心。对于数对的第二个数从小到大排序,然后从一个开始寻找符合条件数对,遍历完即可得到答案。代码如下。
class Solution {
public:int findLongestChain(vector<vector<int>>& pairs) {sort(pairs.begin(), pairs.end(), [](vector<int> v1, vector<int> v2)->bool{return v1[1] < v2[1];});int pre = -2000;int ans = 0;int length = pairs.size();for(int i = 0;i < length;++i){if(pairs[i][0] > pre){pre = pairs[i][1];++ans;}}return ans;}
};
题目五
测试链接:https://www.luogu.com.cn/problem/P8776
分析:这道题需要理解,被修改的数和被修改为的数紧靠在一起求得的答案和原题目没有区别。这样被修改的数和被修改为的数就组成了一个子数组。对子数组的左边,求得以子数组左边下标为结尾的最长不下降子序列的长度且这个最长不下降子序列的最大值不能超过这个被修改为的数;对于这个子数组的右边,求得以子数组右边为开头的最长不下降子序列的长度。将两个最长不下降子序列的长度相加再加上k就是这个子数组求得的答案,遍历数组即可得到最大值。代码如下。
#include <iostream>
using namespace std;
int N, K;
int arr[100000];
int Right[100000];
int End[100000];
int get_index1(int num, int len){int ans = -1;int m, l = 0, r = len - 1;while (l <= r){m = (l + r) / 2;if(End[m] < num){ans = m;r = m - 1;}else{l = m + 1;}}return ans;
}
void get_right(){End[0] = arr[N-1];int len = 1;Right[N-1] = 1;for(int i = N-2, find;i >= K;--i){find = get_index1(arr[i], len);if(find == -1){End[len++] = arr[i];Right[i] = len;}else{End[find] = arr[i];Right[i] = find+1;}}
}
int get_index2(int num, int len){int ans = -1;int m, l = 0, r = len - 1;while (l <= r){m = (l + r) / 2;if(End[m] > num){ans = m;r = m - 1;}else{l = m + 1;}}return ans;
}
int main(void){scanf("%d%d", &N, &K);for(int i = 0;i < N;++i){scanf("%d", &arr[i]);}get_right();int ans = K + Right[K];End[0] = arr[0];int len = 1;for(int i = K+1, find;i < N;++i){find = get_index2(arr[i], len);find = find == -1 ? len : find;ans = ans > (find + K + Right[i]) ? ans : (find + K + Right[i]);find = get_index2(arr[i-K], len);if(find == -1){End[len++] = arr[i-K];}else{End[find] = arr[i-K];}}printf("%d", ans);
}
相关文章:
算法【最长递增子序列问题与扩展】
本文讲解最长递增子序列以及最长不下降子序列的最优解,以及一些扩展题目。本文中讲述的是最优解,时间复杂度是O(n*logn),空间复杂度O(n),好实现、理解难度不大。这个问题也可以用线段树来求解,时间和空间复杂度和本节讲…...
k8s篇之flannel网络模型详解
在 Kubernetes (K8s) 中,Flannel 是一种常用的网络插件,用于实现容器之间的网络通信。Flannel 提供了一种覆盖网络(Overlay Network)模型,使得容器可以跨多个主机进行通信。 以下是 Flannel 在 Kubernetes 中的详细工作原理和覆盖网络模型的详解: 1.Flannel 简介 Flann…...
windows 和 linux检查操作系统基本信息
windows检查操作系统基本信息 systeminfolinux检查操作系统基本信息 获取系统位数 getconf LONG_BIT查询操作系统release信息 lsb_release -a查询系统信息 cat /etc/issue查询系统名称 uname -a...
Oracle OCP认证考试考点详解082系列22
题记: 本系列主要讲解Oracle OCP认证考试考点(题目),适用于19C/21C,跟着学OCP考试必过。 105. 第105题: 题目 解析及答案: 题目翻译: 关于Oracle数据库中的事务请选择两个正确的陈述…...
线性回归 - 最小二乘法
线性回归 一 简单的线性回归应用 webrtc中的音视频同步。Sender Report数据包 NTP Timestamp(网络时间协议时间戳):这是一个64位的时间戳,记录着发送SR的NTP时间戳,用于同步不同源之间的时间。RTP Timestamp࿱…...
Linux - 线程基础
文章目录 1.什么是线程2.线程vs进程3.线程调度4.线程控制4.1 POSIX线程库4.2创建线程4.3线程终止4.4线程等待4.5线程分离 5、线程封装 1.什么是线程 在Linux操作系统中,线程是进程内部的一个执行流。在Linux操作系统下,执行流统称为轻量级进程࿰…...
网络爬虫——分布式爬虫架构
分布式爬虫在现代大数据采集中是不可或缺的一部分。随着互联网信息量的爆炸性增长,单机爬虫在性能、效率和稳定性上都面临巨大的挑战。分布式爬虫通过任务分发、多节点协作以及结果整合,成为解决大规模数据抓取任务的核心手段。 本节将从 Scrapy 框架的…...
RT_Thread内核源码分析(三)——线程
目录 1. 线程结构 2. 线程创建 2.1 静态线程创建 2.2 动态线程创建 2.3 源码分析 2.4 线程内存结构 3. 线程状态 3.1 线程状态分类 3.2 就绪状态和运行态 3.3 阻塞/挂起状态 3.3.1 阻塞工况 3.4 关闭状态 3.4.1 线程关闭接口 3.4.2 静态线程关闭 3.4.3 动态线程关…...
正排索引和倒排索引
一、简介 正排索引:一个未经处理的数据库中,一般是以文档ID作为索引,以文档内容作为记录。 倒排索引:Inverted index,指的是将单词或记录作为索引,将文档ID作为记录,这样便可以方便地通过单词或…...
丹摩 | 重返丹摩(上)
目录 一.登录平台 二. 数据管理与预处理 1.数据清洗 2.数据格式转换 3.特征工程 二.数据可视化 1.快速可视化 2.数据洞察 3.自定义视图 三.技术支持与帮助 1.技术支持 (1). 帮助文档 (2). 用户社区 2.客服支持 (1). 在线客服 (2). 反馈与建议 总结 一.登录平台…...
Frontend - 防止多次请求,避免重复请求
目录 一、避免重复执行的多种情况 (一)根据用途 (二)根据用户操作 二、具体实现 (一)“Ajax ”结合disabled (防止多次请求),避免多次点击重复请求 1. 适用场景 2. 解决办法 3. 示例 &…...
RHCE的学习(22)
第四章 流程控制之条件判断 条件判断语句是一种最简单的流程控制语句。该语句使得程序根据不同的条件来执行不同的程序分支。本节将介绍Shell程序设计中的简单的条件判断语句。 if语句语法 单分支结构 # 语法1: if <条件表达式> then指令 fi #语法2&#x…...
【前端知识】简单讲讲什么是微前端
微前端介绍 一、定义二、背景三、核心思想四、基本要素五、核心价值六、实现方式七、应用场景八、挑战与解决方案 什么是single-spa一、核心特点二、核心原理三、应用加载流程四、最佳实践五、优缺点六、应用场景 什么是 qiankun一、概述二、特点与优势三、核心功能四、使用场景…...
AWS IAM
一、介绍 1、简介 AWS Identity and Access Management (IAM) 是 Amazon Web Services 提供的一项服务,用于管理 AWS 资源的访问权限。通过 IAM,可以安全地控制用户、组和角色对 AWS 服务和资源的访问权限。IAM 是 AWS 安全模型的核心组成部分,确保只有经过授权的用户和应…...
丹摩|丹摩助力selenium实现大麦网抢票
丹摩|丹摩助力selenium实现大麦网抢票 声明:非广告,为用户体验 1.引言 在人工智能飞速发展的今天,丹摩智算平台(DAMODEL)以其卓越的AI算力服务脱颖而出,为开发者提供了一个简化AI开发流程的强…...
基于Qt/C++/Opencv实现的一个视频中二维码解析软件
本文详细讲解了如何利用 Qt 和 OpenCV 实现一个可从视频和图片中检测二维码的软件。代码实现了视频解码、多线程处理和界面更新等功能,是一个典型的跨线程图像处理项目。以下分模块对代码进行解析。 一、项目的整体结构 项目分为以下几部分: 主窗口 (M…...
智慧理财项目测试文档
目录 幕布思维导图链接:https://www.mubu.com/doc/6xk3c7DzgFs学习链接:https://www.bilibili.com/video/BV15J4m147vZ/?spm_id_from333.999.0.0&vd_source078d5d025b9cb472d70d8fda1a7dc5a6智慧理财项目测试文档项目介绍项目基本信息项目业务特性系…...
R | 统一栅格数据的坐标系、分辨率和行列号
各位同学,在做相关性等分析时,经常会遇到各栅格数据间的行列号不统一等问题,下面的代码能直接解决这类麻烦。以某个栅格数据的坐标系、分辨率和行列号为准,统一文件夹内所有栅格并输出到新的文件夹。 代码只需要更改输入输出和ti…...
C++学习——编译的过程
编译的过程——预处理 引言预处理包含头文件宏定义指令条件编译 编译、链接 引言 C程序编译的过程:预处理 -> 编译(优化、汇编)-> 链接 编译和链接的内容可以查阅这篇文章(点击查看) 预处理 编译预处理是指&a…...
当你要改文件 但是原来的文件内容又不能丢失的时候,拷贝一份(备注原来的),然后添加后缀:.bak
当你要改文件 但是原来的文件内容又不能丢失的时候,拷贝一份(备注原来的),然后添加后缀:.bak !!!文件不要直接删除,若你以后要还原的话会找不到...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
