算法沉淀 —— 动态规划(子序列问题(上))

算法沉淀 —— 动态规划(子序列问题(上))

  • 前言
  • 一、最长递增子序列
  • 二、摆动序列
  • 三、 最长递增子序列的个数
  • 四、最长数对链

前言

几乎所有的动态规划问题大致可分为以下5个步骤,后续所有问题分析都将基于此

  • 1.、状态表示:通常状态表示分为以下两种,其中更是第一种为主。

    • 以i为结尾,dp[i] 表示什么,通常为代求问题(具体依题目而定)
    • 以i为开始,dp[i]表示什么,通常为代求问题(具体依题目而定)
  • 2、状态转移方程

    • 以上述的dp[i]意义为根据, 通过最近一步来分析和划分问题,由此来得到一个有关dp[i]的状态转移方程。
  • 3、dp表创建,初始化

    • 动态规划问题中,如果直接使用状态转移方程通常会伴随着越界访问等风险,所以一般需要初始化。而初始化最重要的两个注意事项便是:保证后续结果正确,不受初始值影响;下标的映射关系
    • 初始化一般分为以下两种:
      • 直接初始化开头的几个值。
      • 一维空间大小+1,下标从1开始;二维增加一行/一列
  • 4、填dp表、填表顺序:根据状态转移方程来确定填表顺序。

  • 5、确定返回值

一、最长递增子序列

【题目链接】:300. 最长递增子序列
【题目】:

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组[0,3,1,6,2,2,7] 的子序列。

【示例】:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

【分析】:
 我们可以定义dp[i]表示以i位置为结尾的最长递增子序列。由于每一个数字均可单独构成一个递增子序列,所以我们在创建dp表时,可以将dp表中的值全部初始化为1.

状态转移方程推导:

 如果dp[i]所表示的递增子序列大于1,此时在i之前一定存在某些元素和nums[i]构成递增子序列。而这些元素我们可以用dp[j[表示(0 <= j <i),并且nums[j] < nums[i]。
 但j到底在什么位置呢?我们无从而知,需要依次遍历i前的数据。来查找符合要求的最大dp[j]。所以状态转移方程为:

for(int j = i - 1; j >= 0; j--)
{if(nums[i] > nums[j])dp[i] = max(dp[i], dp[j] + 1);}

【其他】:
 最后只需将dp[i]的最终值累加即可!!

【代码编写】:

class Solution {
public:int lengthOfLIS(vector<int>& nums) {int n = nums.size();int ret = 1;vector<int> dp(n, 1);for(int i = 1; i < n; i++){for(int j = i - 1; j >= 0; j--){if(nums[i] > nums[j])dp[i] = max(dp[i], dp[j] + 1);}ret = max(dp[i], ret);}return ret;}
};

二、摆动序列

【题目链接】:376. 摆动序列
【题目】:

 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
 子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
 给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。

【分析】:
 我们可以定义dp[i]表示以i位置为结尾的最大摆动序列。但我们发现i位置的状态还可以细分为上升和下降。所以我们重新定义:

  1. f[i]表示以i位置为结尾,且最后呈 ”上升“ 趋势的最大摆动序列。
  2. g[i]表示以i位置为结尾,且最后呈 ”趋势“ 的最大摆动序列。

状态转移方程推导:

  1. f[i]:最大摆动序列的长度可能为1,也可能大于1。对于大于1的情况,由于f[i]表示最后位置是呈上升趋势的摆动序列,所以nums[i] > nums[i - 1]。此时在i-1位置一定是呈下降的,即g[i-1]。所以状态转移方程如下:

在这里插入图片描述

  1. 对于g[i]来说,同理可得状态转移方程如下:
    在这里插入图片描述

细节处理:

 不管是f表还是g表,以i位置为结尾的摆动序列的最小长度一定为1,即nums[i]本身。所以我们可以直接将f表和g表中的数据的初始值设为1。后续填表过程中,仅需考虑长度大于1的情况即可!!

【代码编写】:

class Solution {
public:int wiggleMaxLength(vector<int>& nums) {int n = nums.size(), ret = 1;vector<int> f(n, 1), g(n, 1);for(int i = 1; i < n ; i++){for(int j = i - 1; j >= 0; j--){if(nums[i] > nums[j])f[i] = max(g[j] + 1, f[i]);else if(nums[i] < nums[j])g[i] = max(f[j] + 1, g[i]);}ret = max(ret, max(f[i], g[i]));}return ret;}
};

三、 最长递增子序列的个数

【题目链接】:673. 最长递增子序列的个数
【题目】:

给定一个未排序的整数数组 nums , 返回最长递增子序列的个数 。
注意 这个数列必须是 严格 递增的。

【示例】:

输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。

【分析】:

 我们先定义dp[i]表示以i位置为结尾的最长递增子序列的个数。但我们发现dp[i]表示两个状态:最长递增、个数。所以我们需要将dp[i]状态细分:

  1. 我们定义len[i]表示以i位置为结尾的最长递增子序列的长度。
  2. 定义count[i]表示以i位置为结尾的最长递增子序列的个数。

 显然len[i]、count[i]最小为1,即nums[i]本身单独构成一个递增子序列。所以我们在创建len表和count表时,我们可以将初始值设为1。
 如果最大递增子序列长度大于1时,这也意味着在i前就已经存在一个递增子序列(设该子序列以j下标为结尾)。并且满足nums[i] > nums[j],此时该递增子序列(以j下标结尾,0 <= j < i)和nums[i]构成了一个新的递增子序列。
 如果最长递增子序列大于1,此时最终结果不仅要保证nums[i] > nums[j],还需保证以j位置结尾的子序列最大(即len[j]在0 ~ i-1中最大)。所以我们需要依次遍历0 ~ i-1之间len表中的左右值!!

那count[i]的值如何更新呢?

 我们可以在求len[i]的过程中,更新count[i]的值。具体如下:

  • 如果len[i] + 1 == len[j],此时count[i] += count[j]
  • 如果len[i] + 1 > len[j],此时len[i]和count[i]的需要重新更新。即len[i] = len[j] + 1, count[i] = count[j]
  • 如果len[i] + 1 < len[j],此时count[j]是无效次数。我们直接忽略这种情况。

细节处理:
 通过上述过程我们可以的以i位置为结尾的最大递增序列和次数。所以最终整个数组中,构成的最大递增序列的次数求解可以采用和上述类型的思想。(具体查看代码)

【代码编写】:

class Solution {
public:int findNumberOfLIS(vector<int>& nums) {int n = nums.size();vector<int> len(n, 1), count(n, 1);int maxlen = 1, maxcount = 1;for(int i = 1; i < n; i++){for(int j = i - 1; j >= 0; j--){if(nums[i] > nums[j]){if(len[j] + 1 == len[i])count[i] += count[j];else if(len[j] + 1 > len[i])len[i] = len[j] + 1, count[i] = count[j];}}if(len[i] == maxlen)maxcount += count[i];else if(len[i] >maxlen)maxlen = len[i], maxcount = count[i]; }return maxcount;}
};

四、最长数对链

【题目链接】:646. 最长数对链
【题目】:

 给你一个由 n 个数对组成的数对数组 pairs ,其中 pairs[i] = [lefti, righti] 且 lefti < righti 。
 现在,我们定义一种 跟随 关系,当且仅当 b < c 时,数对 p2 = [c, d] 才可以跟在 p1 = [a, b] 后面。我们用这种形式来构造 数对链 。
 找出并返回能够形成的 最长数对链的长度 。你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。

【分析】:
 由于题目中明确表示可以任意选择数组中的数来构造数对链。所以我们可以先对数组进行排序。此时我们可以定义dp[i[表示以i位置为结尾的最长数对链。

状态转移方程推导分析:

 显然dp[i]的最小值为1,即[pair[i][0], pair[i][1]]本身构成数对链。
 如果以i位置为结尾的数对链的长度大于1,此时i位置之前一定存在数对链(下标设为j,0 <= j <i)和当前元素构成一个数对链。该情况的出现需满足pairs[i][0] > pairs[j][1]。但我们最终的结果是要得到dp[j]中的最大值。我们可以直接从后往前遍历,只要查找到第一个满足条件的dp[j]即可。(原因在于我们已经对原数组进行排序)
所以状态转移方程为:

 for(int j = i -1; j >= 0; j--){if(pairs[i][0] > pairs[j][1]){dp[i] = max(dp[i], dp[j] + 1);break;}}

【代码编写】:

class Solution {
public:int findLongestChain(vector<vector<int>>& pairs) {sort(pairs.begin(), pairs.end());int n = pairs.size(), ret = 1;vector<int> dp(n, 1);for(int i = 1; i < n; i++){for(int j = i -1; j >= 0; j--){if(pairs[i][0] > pairs[j][1]){dp[i] = max(dp[i], dp[j] + 1);break;}}ret = max(ret, dp[i]);}return ret;}
};

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

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

相关文章

BigInteger 大整数 比较大小

一、以整数型礼品交易为例子 int userSend Integer.valueOf(id);int amount Integer.valueOf(amountStr);int userAccept Integer.valueOf(userIdAccept);GiftService giftService new GiftService();boolean carry1 giftService.isHavePropertyByUserIdByGiftId(userSend…

Minikube本地搭建单节点Kubernetes集群

1、什么是 Minikube Minikube 是一个开源工具&#xff0c;旨在为开发者提供一种便捷的方式在本地环境中搭建单节点的 Kubernetes 集群。它主要用于开发、测试和学习 Kubernetes 应用程序&#xff0c;无需依赖大型的硬件资源或复杂的多节点集群配置。minikube 使用轻量级虚拟化技…

C/C++程序的(编译,链接)翻译与运行

目录 前言&#xff1a; 1.程序环境 2.翻译环境 3.预处理&#xff08;预编译&#xff09; 4.编译 5.汇编 6.链接 7.运行环境 总结&#xff1a; 前言&#xff1a; 本篇来解释c/c程序的翻译环境与运行环境中的过程&#xff0c;不同的编程语言的翻译环境类似&#xff0c;…

Transformers in Vision:A Survey 阅读笔记

ACM上的一篇综述&#xff0c;讨论Transformer在CV上的应用。 摘要&#xff1a; Among their salient benefits,Transformers enable modeling long dependencies between inputsequence elements and support parallel processing of sequence as compared to recurrent networ…

信息传播的AI时代:机器学习赋能新闻出版业的数字化之旅

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导…

leetcode90. 子集 II

去重逻辑&#xff1a; 关键是画出递归树&#xff01;当我们即将进入第二个2的递归的时候&#xff0c;发现isVisit数组是100&#xff0c;也就是说这俩重复的数是False&#xff0c;并且这俩在nums值相同&#xff0c;所以写出去重逻辑&#xff01; class Solution { public:vector…

leetcode代码记录(打家劫舍 II

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 一个专业的小偷&#xff0c;计划偷窃一个环形街道上沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房屋是…

【随笔】Git -- 高级命令(上篇)(六)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

【面试八股总结】超文本传输协议HTTP(二)

参考资料 &#xff1a;小林Coding、阿秀、代码随想录 一、HTTP缓存技术 将资源&#xff08;如网页、图像、脚本等&#xff09;的副本存储在客户端或中间代理服务器上&#xff0c;以便将来的请求可以直接从缓存中获取&#xff0c;而不必重新从服务器下载资源。这有助于减少网…

二维码:技术、商业与未来

title: 二维码&#xff1a;技术、商业与未来 date: 2024/4/3 19:12:28 updated: 2024/4/3 19:12:28 tags: 二维码技术商业应用移动支付物联网AR/VR融合智能家居数字化社会 第一章&#xff1a;引言 1. 二维码在数字化时代的重要性和普及程度 在数字化时代&#xff0c;二维码作…

JVM之常用监控工具

JVM之常用监控工具 jps jinfo 获取配置信息 基本语法 jinfo [options] <pid>常用选项 -sysprops&#xff1a;显示JVM进程的系统属性。-flags&#xff1a;显示用于启动JVM的命令行标志和VM选项。-flag <name>&#xff1a;显示指定JVM标志的当前值。-flag [|-]&…

Windows10安装CloudCompare(图文安装)

CloudCompare是一个3D点云&#xff08;和三角网格&#xff09;处理软件。它最初被设计用于在两个密集的3D点云&#xff08;例如用激光扫描仪获取的点云&#xff09;之间或点云和三角形网格之间进行比较。它依赖于专用于此任务的特定八叉树结构。 之后&#xff0c;它已经扩展到一…

[实验报告]--基于端口安全

[实验报告] 目录 [实验报告] 一、项目背景 二、实验环境 三、项目规划设计 四、项目实施 五、验证项目成果 基于端口安全的 Jan16 公司网络组建 一、项目背景 Jan16 公司开发部为重要部门&#xff0c;所有员工使用指定的计算机工作&#xff0c;为防止员工或访客使 用个…

java中Date类,SimpleDateFormat类和Calendar类

Date类 public Date() 创建一个Date对象&#xff0c;代表的是系统当前此刻的日期时间 public Date(long date) Constructs a Date object using the given milliseconds time value. 把时间毫秒值转变成Date日期对象 public void setTime(long date) Sets an existing Date ob…

深度解析大语言模型中的词向量

在学习完语言模型中的编码器与解码器知识后&#xff0c;让我们继续深入学习大语言模型中一个非常关键的技术&#xff1a;词向量表示&#xff0c;以及如何通过Transformer模型实现对next token的预测。 1、词向量 要了解语言模型的工作原理&#xff0c;首先需要了解它们如何表示…

C#.net6.0手术麻醉信息管理系统源码,智慧手术室管理平台源码

手术麻醉信息管理系统源码&#xff0c;自主版权的手麻系统源码 手术麻醉信息管理系统包含了患者从预约申请手术到术前、术中、术后的流程控制。手术麻醉信息管理系统主要是由监护设备数据采集子系统和麻醉临床系统两个子部分组成。包括从手术申请到手术分配&#xff0c;再到术前…

element-ui collapse 组件源码分享

今日简单分享 collapse 组件的源码实现&#xff0c;主要分为四个方面&#xff1a; 1、collapse 组件页面结构 2、collapse 组件属性 3、collapse 组件事件 4、collapse item 组件属性 一、collapse 组件页面结构 二、collapse 组件属性 2.1 value/v-model 属性&#xff0…

Ubuntu18.04+2070s+TF2.x环境,单卡训练PointNet++实战

Ubuntu18.042070sTF2.x环境&#xff0c;单卡训练PointNet实战 1. 编译tf_ops文件夹下的三个动态库2. 修改Python版本、TF版本不一致带来的差异3. 下载训练数据4. 模型训练 1. 编译tf_ops文件夹下的三个动态库 该文件夹下定义了一些pointnet模型中需要使用的cuda核函数&#xf…

程序代码分析工具

文章目录 工具简介和安装DoxygenGraphziv软件安装 工具的运用启动和配置工具分析结果 工具简介和安装 Doxygen Doxygen 是一种用于从 C 、C 、Objective-C 、C# 、Java 和 Python 等语言的源代码中生成文档的工具。它通过解析源代码中的注释来创建详细的 API 文档&#xff0c;…

码农失业倒计时?全球首个大厂AI程序员来了

进入互联网时代&#xff0c;程序员作为高收入职业的代表&#xff0c;长久以来一直是众多求职者梦寐以求的工作方向。程序员们凭借其对计算机科学的深刻理解和技术创新能力&#xff0c;不仅推动了科技的进步&#xff0c;也为自己赢得了可观的经济回报。 然而&#xff0c;随着人…