DP:子序列模型

子数组vs子数列

1、子数组(n^2)    子序列(2^n)       

2、子数组是子序列的一个子集

3、子数组必须连续,子序列可以不连续

一、最长递增子序列

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i]表示以i位置为结尾所有子系列中,最长递增子序列的长度。 

 2、状态转移方程

(1)长度为1——>dp[i]=1

  (2)   长度大于1——>满足前提(nums[j]<nums[i])——>max(dp[j]+1,dp[i])  (0<=j<=i-1)

3、初始化

如果无法更新,最差情况自己也是一个子序列,所以dp表全都初始化为1

4、填表顺序

需要借助前面的状态,所以要从左往右

5、返回值

dp表中的最大值——>可以用ret去更新出最大值,也可以用*max_element(dp.begin(),dp.end())

6、复杂度

时间复杂度:N^2 (因为是子序列而非子数组,所以当我们固定住i的时候,他的前面可以是i-1、i-2、i-3…… 所以需要遍历一遍更新出最大的长度)

空间复杂度:N

class Solution {
public:int lengthOfLIS(vector<int>& nums) {int n=nums.size();vector<int> dp(n,1);for(int i=1;i<n;++i)for(int j=0;j<i;++j)if(nums[j]<nums[i]) dp[i]=max(dp[j]+1,dp[i]); //前提条件要满足return *max_element(dp.begin(),dp.end());//dp数组中的最大值}
};

二、摆动序列

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i]表示以i位置为结尾所有子序列中,最长递增子序列的长度。 (错误)

因为会存在两种状态,所以我们需要两个dp数组:

f[i]表示以i位置为结尾的所有子序列中,最后一个位置呈现“上升”趋势的最长摆动序列的长度

g[i]表示以i位置为结尾的所有子序列中,最后一个位置呈现“下降”趋势的最长摆动序列的长度

 2、状态转移方程

f[i](上升):

(1)长度为1——>1

  (2)   长度大于1——>满足前提(nums[j]<nums[i])——>max(g[j]+1,f[i])  (0<=j<=i-1)

g[i](下降):

(1)长度为1——>1

  (2)   长度大于1——>满足前提(nums[j]>nums[i])——>max(f[j]+1,g[i])  (0<=j<=i-1)

3、初始化

如果无法更新,最差情况自己也是一个子序列,所以g表和f表全都初始化为1

4、填表顺序

需要借助前面的状态,所以要从左往右,两个表一起填

5、返回值

两个表中的最大值

class Solution {
public:int wiggleMaxLength(vector<int>& nums) {//f[i]表示以i位置结尾的最长子序列中,最后呈现上升趋势 (前提nums[i]<nums[j])//g[i]表示以i位置结尾的最长子序列中,最后成下降趋势    (前提nums[i]>nums[j])int n=nums.size();vector<int> f(n,1),g(n,1);for(int i=1;i<n;++i)for(int j=0;j<i;++j)if(nums[i]<nums[j])  g[i]=max(f[j]+1,g[i]);else if(nums[i]>nums[j]) f[i]=max(g[j]+1,f[i]);return max(*max_element(f.begin(),f.end()),*max_element(g.begin(),g.end()));}
};

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

. - 力扣(LeetCode)

在讲解前先来个小demo:如何在数组中找出最大值出现的次数

方案1:第一次for循环确定最大的值是多少,第二次for循环统计最大的值出现了几次

方案2:利用贪心策略一次for循环搞定(定义maxval记录当前的最大值,count统计数量)

(1)x==maxval:++count 

(2)x<maxval:直接无视

(3)x>maxval:更新最大值——>maxval=x,然后重新计数——>count=1

算法原理:

1、状态表示(经验+题目要求)

dp[i]表示以i位置为结尾所有子序列中,最长递增子序列的个数。 (错误)

因为我们在填表的时候并不能确认最长递增子序列的长度是多少,所以无法直接统计。我们就得用demo中的方案2的思想,来解决这个问题。

len[i]表示以i位置为结尾的所有子序列中,最长递增子序列的“长度”

count[i]表示以i位置为结尾的所有子序列中,最长递增子序列的“个数”

 2、状态转移方程

nums[j]<nums[i]时

(1)len[j]+1==len[i]——>count[i]+=count[j]

(2)len[j]+1<len[i] 无视

(3)len[j]+1>len[i]——>len[i]=len[j]+1  count[i]=count[j](更新最大值并重新计数)

3、初始化

全都初始化为1

4、填表顺序

需要借助前面的状态,所以要从左往右,两个表一起填

5、返回值

recount统计结果

class Solution {
public:int findNumberOfLIS(vector<int>& nums) {int n=nums.size();vector<int> len(n,1),count(n,1); //count统计以i位置结尾时最长子序列的个数 len是长度int retlen=1,recount=1;//统计最大长度和最大长度的个数for(int i=1;i<n;++i){for(int j=0;j<i;++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(retlen==len[i]) recount+=count[i];else if(retlen<len[i]){retlen=len[i];recount=count[i];}}return recount;}
};

四、最长数链对

. - 力扣(LeetCode)

算法原理:

预处理:由于题目要求是任意顺序组成数链对,所以我们在处理的时候不仅要考虑前面,还要考虑后面,这样不利于我们的动态规划表示,所以我们要先按照第一个元素进行排序(比如[a,b] [c,d],a<c<d 所以d>a,所以后面的不需要考虑到),我们要进行sort,在C++中,vector、pair的默认比较逻辑都是按照字典序的,恰好符合我们的要求,所以我们可以直接调用sort。

1、状态表示(经验+题目要求)

dp[i]表示以i位置为结尾所有数对链序列中,最长数对链的长度。 

 2、状态转移方程

dp[i]:

(1)长度为1——1

(2)长度大于1——p[j][1]>p[i][0] ——max(dp[i],dp[j]+1)

3、初始化

初始化为1

4、填表顺序

需要借助前面的状态,所以要从左往右

5、返回值

要返回dp表中的最大值,但由于我们排序过,所以最大值必然出现在dp[n-1]。

class Solution {
public:int findLongestChain(vector<vector<int>>& pairs) {//vector的排序就像字典序一样//预处理直接sort 正好符合我们的要求sort(pairs.begin(),pairs.end());int n=pairs.size();vector<int> dp(n,1);for(int i=1;i<n;++i)for(int j=0;j<i;++j)if(pairs[j][1]<pairs[i][0]) dp[i]=max(dp[j]+1,dp[i]);//(1,5)(2,3)(4,10)(5,9)return dp[n-1];//最大值必然在最后面}
};

五、最长定差子序列(经典)

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i]表示以i位置为结尾所有子序列中,最长的等差子序列长度 

 2、状态转移方程

dp[i]:

(1)b不存在——>1

(2)b存在——>取最后一个即可dp[j]+1

我们会发现前一个数基本上是可以确定是多少的,并且有多个的话也可以用后面的覆盖前面的,因此我们可以用哈希表做优化

优化思路:

(1)将元素+dp[i]的值存在哈希表中

(2)直接在哈希表中做动态规划

3、初始化

hash[arr[0]]=1

4、填表顺序

从左往右

5、返回值

dp表里的最大值

class Solution {
public:int longestSubsequence(vector<int>& arr, int difference) {//数据量太大了O(n^2)必然会超时int n=arr.size();unordered_map<int,int> hash;//第一个是元素,第二个是以这个元素为结尾时的最长等差子序列长度int ret=1;for(int&v:arr) //为了降低时间复杂度,我们发现这道题只需要最后的那个相同的{hash[v]=hash[v-difference]+1; //因为v-difference不在的时候,会被自己创建出来并初始化为0ret=max(ret,hash[v]);}return ret;}
};

    为什么哈希表不需要先将数组中的元素全部初始化为1???因为hash[v]=hash[v-difference]+1,当v-differences不存在的时候,重载方括号会去调用insert并允许我们修改second,在创建的时候初始化了。

六、最长的斐波那契子序列长度

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i]表示以i位置为结尾所有子序列中,最长的斐波那契子序列长度(错误)。

因为我们至少得确定两个位置,才能知道序列是否满足斐波那契子序列的要求。

dp[i][j]表示以i位置及j位置为结尾所有子序列中,最长的斐波那契子序列长度。

 2、状态转移方程

dp[i][j]:  (假设abc对应的坐标分别是kij)

(1)如果a存在且a<b——>dp[k][i]+1

(2)a存在且b<a<c——>2

(3)a不存在——>2

       我们固定两个数用了两层for循环了,如果找第三个数的时候还要在前面用一层for循环的话,那么就是n^3的时间复杂度了,所以我们可以用哈希表来帮助我们存储下标和元素的映射关系。并且我们只需要保存靠后的元素下标即可。

优化思路:将元素与下标绑定存放在哈希表中。

3、初始化

都初始化为2

4、填表顺序

从左往右

5、返回值

dp表里的最大值ret 但是如果ret是2的话就返回0

class Solution {
public:int lenLongestFibSubseq(vector<int>& arr) {//必须通过元素快速找到dp表对应的下标 unordered_map<int,int> hash;//哈希帮助我们快速定位int n=arr.size();for(int i=0;i<n;++i) hash[arr[i]]=i;int ret=2;//起始为2vector<vector<int>> dp(n,vector<int>(n,2));//得知道两个位置,才能确定前面的for(int j=2;j<n;++j) //固定最后的位置for(int i=1;i<j;++i)//固定倒数第2个位置{int a=arr[j]-arr[i];if(hash.count(a)&&a<arr[i]) dp[i][j]=dp[hash[a]][i]+1;ret=max(dp[i][j],ret);}return ret==2?0:ret;}
};

七、最长等差数列

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i]表示以i位置为结尾所有子序列中,最长的等差子序列的长度(错误)。

因为我们至少得确定两个位置,才能知道序列是否满足等差子序列的要求。

dp[i][j]表示以i位置及j位置为结尾所有子序列中,最长的等差子序列长度。

 2、状态转移方程

dp[i][j]:  (假设abc对应的坐标分别是kij)

(1)如果a存在且a<b——>dp[k][i]+1

(2)a存在且b<a<c——>2

(3)a不存在——>2

       我们固定两个数用了两层for循环了,如果找第三个数的时候还要在前面用一层for循环的话,那么就是n^3的时间复杂度了,所以我们可以用哈希表来帮助我们存储下标和元素的映射关系。并且我们只需要保存靠后的元素下标即可。

优化思路:

(1)将元素与下标绑定存放在哈希表中。

(2)i位置填完后,将i位置的值放进哈希表中

3、初始化

都初始化为2

4、填表顺序

有两种方式(选择方法2

(1)先固定最后一个数,然后枚举倒数第二个数(必须在dp前先将元素与下标的关系绑定再哈希表中,到时候在找的时候还得判断b<a<c的情况)

(2)先固定倒数第二个数,再枚举最后一个数(可以等i位置填完后再将i的位置丢进哈希表,这样可以保证哈希表内的元素的下标必然是小的,就可以不需要判断b<a<c的情况)

5、返回值

dp表里的最大值ret

class Solution {
public:int longestArithSeqLength(vector<int>& nums) {//得确定至少两个位置,通过这两个位置求出了等差是多少,才能确定最终的结果unordered_map<int,int> hash;int n=nums.size();hash[nums[0]]=0;//必须要先固定两个数int ret=2;vector<vector<int>> dp(n,vector<int>(n,2));for(int i=1;i<n;++i) //边遍历边丢,这样就能确保哈希表里面的都是前面的元素{for(int j=i+1;j<n;++j){int a=2*nums[i]-nums[j];if(hash.count(a)) dp[i][j]=dp[hash[a]][i]+1;ret=max(dp[i][j],ret);}hash[nums[i]]=i;//记住最后一个即可}return ret;}
};

八、等差数列划分II-子序列 

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i]表示以i位置为结尾所有子序列中,最长的等差子序列的长度(错误)。

因为我们至少得确定两个位置,才能知道序列是否满足等差子序列的要求。

dp[i][j]表示以i位置及j位置为结尾所有子序列中,最长的等差子序列长度。

 2、状态转移方程

dp[i][j]:  (假设abc对应的坐标分别是kij)

(1)如果a存在且a<b——>dp[i][j]+=dp[k][i]+1

(2)a存在且b<a<c——>0

(3)a不存在——>0

       我们固定两个数用了两层for循环了,如果找第三个数的时候还要在前面用一层for循环的话,那么就是n^3的时间复杂度了,所以我们可以用哈希表来帮助我们存储下标和元素的映射关系。并且我们只需要保存靠后的元素下标即可。

优化思路:

(1)将元素与下标绑定存放在哈希表中。(该题需要统计所有的子序列,所以相同元素下标不同的情况都要统计,因此我们要将元素绑定一个下标数组)

(2)i位置填完后,将i位置的值放进哈希表中

3、初始化

都初始化为0

4、填表顺序

       先固定倒数第二个数,再枚举最后一个数(可以等i位置填完后再将i的位置丢进哈希表,这样可以保证哈希表内的元素的下标必然是小的,就可以不需要判断b<a<c的情况)

5、返回值

dp表的总和——用一个sum去统计

 6、细节处理

可能会溢出,所以我们要下标要存储long

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

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

相关文章

数据管理积重难返?这有一个新药方丨直播预告

大数据产业创新服务媒体 ——聚焦数据 改变商业 在数智化转型的浪潮中&#xff0c;数据管理领域正面临着前所未有的挑战和机遇。企业在数据管理过程中&#xff0c;普遍遭遇数据孤岛、数据质量不佳、存储和处理成本高昂、数据安全与隐私保护压力以及多源异构数据整合困难等诸多…

【MMU】——MMU 页命中/缺页

文章目录 MMU 页命中/缺页MMU 命中MMU 缺页 MMU 页命中/缺页 MMU 命中 处理器产生一个虚拟地址。MMU生成 PTE 地址&#xff0c;并从高速缓存/主存请求得到它。高速缓存/主存向 MMU 返回 PTE。MMU 构造物理地址&#xff0c;并把它传送给高速缓存/主存。高速缓存/主存返回所请求…

SpringBoot引入WebSocket依赖报ServerContainer no avaliable

1、WebSocketConfig 文件报错 Configuration EnableWebSocket public class WebSocketConfig {Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}2、报错内容 Exception encountered during context initialization - canc…

SpringBoot+Vue校园管理系统(前后端分离)

技术栈 JavaSpringBootMavenMyBatisMySQLVueElement-UIShiro 系统角色 管理员用户院系管理员 系统功能截图

《互联网政务应用安全管理规定》电子邮件安全如何整改?

继上篇文章&#xff08;解读《互联网政务应用安全管理规定》网络和数据安全中的身份认证和审计合规&#xff09;之后&#xff0c;本篇文章继续解读第五章“电子邮件安全”&#xff0c;为党政机关事业单位提供电子邮件系统整改思路。 “电子邮件安全”内容从第三十一条到第三十…

VirtualBox 虚拟机中的 centos7 系统拉取 docker 镜像常见报错及解决方法

一、拉取镜像时报错&#xff1a;Error response from daemon: Get "https://registry-1.docker.io/v2/": tls: failed to verify certificate: x509: certificate signed by unknown authority 原因&#xff1a;&#xff08;文心一言给出的原因&#xff09; 这个错误…

如何通过 4 种方式备份和恢复Android联系人

毫无疑问&#xff0c;联系人是Android手机上存储的最重要的信息之一。为了保护这些重要数据&#xff0c;明智的做法是对Android手机进行联系人备份。如果您的手机发生任何情况导致数据丢失&#xff0c;例如被盗、系统崩溃或物理损坏&#xff0c;您可以再次将备份中的联系人恢复…

c# 下 ScintillaNET 显示XML信息并折叠节点

winform下显示XML信息&#xff08;非WPF&#xff09; 之前使用的是FastColoredTextBox&#xff0c;github地址如下&#xff1a; https://github.com/PavelTorgashov/FastColoredTextBox 但是有个问题&#xff0c;它支持中文&#xff0c;wordwraptrue&#xff0c;自动换行时&…

玩物科技:引领物联网时代的创新先锋

在深圳这座充满活力和创新精神的城市&#xff0c;有一家年轻而充满潜力的公司正在悄然改变我们的日常生活。深圳市玩物科技有限公司自2017年成立以来&#xff0c;凭借其卓越的技术和创新理念&#xff0c;逐渐成为物联网时代的先锋力量。 玩物科技的愿景与使命 玩物科技的核心…

【vue3响应式原理】

# 源码结构 源码位置是在packages文件件内&#xff0c;实际上源码主要分为两部分&#xff0c;编译器和运行时环境 1. 编译器 compiler-core 核心编译逻辑compiler-dom 针对浏览器平台编译逻辑compiler-sfc 针对单文件组件编译逻辑compiler-ssr 针对服务端渲染编译逻辑 2. 运行时…

使用kafka tools工具连接带有用户名密码的kafka

使用kafka tools工具连接带有用户名密码的kafka 创建kafka连接&#xff0c;配置zookeeper 在Security选择Type类型为SASL Plaintext 在Advanced页面添加如下图红框框住的内容 在JAAS_Config加上如下配置 需要加的配置&#xff1a; org.apache.kafka.common.security.plain.Pla…

企业数字化转型的主要方面有哪些?

本人研究企业数字化转型10余年&#xff0c;为企业软件选型、数字化提供咨询服务&#xff01;目前重点研究低代码数字化转型玩法&#xff0c;力争为各行各业探索出一条最具性价比的数字化方式。 关于“企业数字化转型包括哪些方面”这个问题&#xff0c;咱先来看个例子哈~ 比如…

用负载绿原酸的纳米复合水凝胶调节巨噬细胞表型以加速伤口愈合

引用信息 文 章&#xff1a;Modulating macrophage phenotype for accelerated wound healing with chlorogenic acid-loaded nanocomposite hydrogel. 期 刊&#xff1a;Journal of Controlled Release&#xff08;影响因子&#xff1a;10.8&#xff09; 发表时间&a…

基于pytoch卷积神经网络水质图像分类实战

具体怎么学习pytorch&#xff0c;看b站刘二大人的视频。 完整代码&#xff1a; import numpy as np import os from PIL import Image import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data…

resultType的类型错误

resultType的类型错误&#xff0c;不能是List而应该是对应的返回Bean对象的类型&#xff0c;VO 这里是引用 org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: Error querying database. Cause: java.lang…

opencv进阶 ——(十二)基于三角剖分实现人脸对齐

三角剖分概念 三角剖分&#xff08;Triangulation&#xff09;是一种将多边形或曲面分解为一系列互不相交的三角形的技术&#xff0c;它是计算几何、计算机图形学、地理信息系统、工程和科学计算中的一个基本概念。通过三角剖分&#xff0c;复杂的形状可以被简化为基本的三角…

病理级Polymer酶标二抗IHC试剂盒上线!

免疫组织化学 Immunohistochemistry,lHC 是利用抗体与抗原特异性识别原理&#xff0c;对组织样本中的抗原进行定位/定性分析的实验技术。组织切片保留了样品的解剖学结构特征&#xff0c;从而可以高分辨率地显现蛋白在细胞&#xff0c;甚至细胞器中的定位。基于以上特性&…

Apple - Image I/O Programming Guide

翻译自&#xff1a;Image I/O Programming Guide&#xff08;更新时间&#xff1a;2016-09-13 https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/ImageIOGuide/imageio_intro/ikpg_intro.html#//apple_ref/doc/uid/TP40005462 文章目录 …

orbslam2代码解读(1):数据预处理过程

写orbslam2代码解读文章的初衷 首先最近陆陆续续花了一两周时间学习视觉slam&#xff0c;因为之前主要是做激光slam&#xff0c;有一定基础所以学的也比较快&#xff0c;也是看完了视觉14讲的后端后直接看orbslam2的课&#xff0c;看的cvlife的课&#xff08;课里大部分是代码…

jenkins的简单使用

2.1.简介 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 2.4.Jenkins安装 1.下载安装包jenkins.war&#xff1b; 2.在安装…