DP:子序列问题

文章目录

  • 什么是子序列
    • 子序列的特点
    • 举例说明
    • 常见问题
  • 关于子序列问题的几个例题
    • 1.最长递增子序列
    • 2.摆动序列
    • 3.最长递增子序列的个数
    • 4.最长数对链
    • 5.最长定差子序列
  • 总结

在这里插入图片描述

什么是子序列

在计算机科学和数学中,子序列(Subsequence)是指从一个序列中删除一些元素(可以是零个或多个),但不改变其余元素相对顺序后形成的新序列。

子序列的特点

元素的相对顺序保持不变。
可以删除零个或多个元素。
一个序列的子序列可以为空序列,即不包含任何元素。

举例说明

设有序列 S = [A, B, C, D, E],则其子序列可以有:
删除零个元素:[A, B, C, D, E](即自身)
删除一个元素:[A, B, C, D]、[A, B, C, E]、[A, B, D, E]、[A, C, D, E]、[B, C, D, E]
删除两个元素:[A, B, C]、[A, B, D]、[A, B, E]、[A, C, D]、[A, C, E]、[A, D, E]、[B, C, D]、[B, C, E]、[B, D, E]、[C, D, E]
删除三个元素:[A, B]、[A, C]、[A, D]、[A, E]、[B, C]、[B, D]、[B, E]、[C, D]、[C, E]、[D, E]
删除四个元素:[A]、[B]、[C]、[D]、[E]
删除所有元素:[](空序列)

常见问题

子序列问题在算法设计和编程竞赛中非常常见。以下是几种经典问题:
最长公共子序列(LCS):给定两个序列,找出它们的最长公共子序列。动态规划是解决这个问题的常用方法。
最长递增子序列(LIS):给定一个序列,找出其中最长的递增子序列。可以使用动态规划或贪心算法结合二分查找解决。
子序列和问题:给定一个序列,找出所有和为特定值的子序列。可以使用回溯法或动态规划解决。

根据我上面的介绍,可以总结,大多数子序列问题其实都可以用DP的算法来解决。

关于子序列问题的几个例题

1.最长递增子序列

题目链接
题目:

在这里插入图片描述

样例 输出和输入:

在这里插入图片描述

首先根据上述子序列的描述,这道题就很容易理解了,就是 让我们求给定数组的最长的递增子序列。
算法原理:
状态表示:dp[i]表示以i位置为结尾的所有子序列中最长的那个子序列的长度。
状态转移方程:
在这里插入图片描述
首先我们要求状态转移方程就要看i位置的状态,我们要确定i位置的状态,是不是应该将0到i-1位置遍历一遍,然后将当中的最长子序列求出来然后再加上当前位置的长度1就可以了,这是当子序列长度大于1的时候,还有一种情况是长度等于1的时候,长度等于1的时候,可以默认看做一个子序列,所以dp[i]就等于1,当长度大于1的时候,这种情况,我们先用一个变量j将0到i-1位置的最长子序列遍历出来,然后再+1,所以状态转移方程:dp[i] = max(dp[j]+1,dp[i])
初始化:因为单独一个元素就可以看做一个递增的子序列,所以DP表中的值可以全部初始化为1.
代码展示:

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

运行结果:
在这里插入图片描述

2.摆动序列

题目链接
题目:

在这里插入图片描述

样例 输出和输入:

在这里插入图片描述

这道题让我们求摆动序列的最长的子序列的长度,首先我们要搞清楚,什么是摆动序列:
在这里插入图片描述

上面就是一个摆动序列。
算法原理:
这道题首先要求摆动序列,我们上个专题已经做过类似的题了,就像湍流数组一样,这道题很显然,我们需要两个状态,一个状态是向下的状态,一个状态是向上的状态,这里定义f[i]是向上的状态,g[i]是向下的状态。
状态表示:f[i]是以i位置为结尾的子序列中长度最长且最后一个状态是向上的最长子序列的长度,g[i]表示以i位置为结尾最后的子序列中最后一个状态向下的最长子序列的长度。
状态转移方程:首先对f[i]分析:在这里插入图片描述
所以这里f[i]的状态转移方程:f[i] = max(g[j] + 1, f[i]),同理也可以求出g[i]的状态转移方程:g[i] = max(f[j] + 1, g[i])
代码展示:

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

运行结果:
在这里插入图片描述

3.最长递增子序列的个数

题目链接
题目:

在这里插入图片描述

样例 输出和输入:

在这里插入图片描述

这道题相对于第一道题换了一个问法。这道题是求最长子序列的个数
算法原理:
状态表示:首先我们先定义一个状态,看这个状态能推下去吗,dp[i]表示以i位置为结尾的所有子序列中,最长子序列的个数。
状态转移方程:首先这里就出问题了 ,这里我们根本不知道最长的子序列是什么,因为根本没有记录的,所以这里根本就推不出来,所以还得加一个len[i],len[i]表示以i位置为结尾的所有子序列中最长子序列的长度,将dp[i]改为count[i],count[i]表示以i位置为结尾的所有子序列中最长的子序列的个数。接下来来推状态转移方程,在这里插入图片描述
有三种情况,当我们遍历的len[j]+1==len[i],意思就是0到i-1位置的子序列中加上当前的长度和之前的最长的子序列是相同的,这里我们应该把以j位置为结尾的最长子序列的个数全部加到count[i]]中。这里画图表示
在这里插入图片描述

根据这些情况可以将表填完,但是,我们还需要 一个retlen和一个retcount更新每次的最长子序列的长度和最长子序列的个数。
这里也分为三种情况,和上面的情况相同,只需要每次遍历完一个位置,更新结果即可。

代码展示:

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) 
    {
        int n = nums.size();
        vector<int> len(n,1), count(n,1);
        //统计每次的每次的最终结果
        int retlen = 1, retcount = 1;
        for (int i = 1;i < n;i++)
        {
            for (int j = i - 1;j >= 0;j--)
            {
                //当出现递增的时候
                if (nums[j] < nums[i])
                {
                    //判断如果加上递增的那一个和当前最长的长度还是一样的则需要更新count
                    if (len[j] + 1 == len[i])count[i] += count[j];
                    //如果加上当前的一个元素比比之前的最长的子序列要长,则重新规划长度
                    else if (len[j] + 1 > len[i])count[i] = count[j],len[i] = len[j];
                }
            }
            //统计每次的结果,如果len和结果的len相同,则直接用count累加
            if (retlen == len[i])
                retcount += count[i];
            //如果len比结果的len要大,则直接重置结果len和结果的count
            else if (retlen < len[i])
                retcount = count[i], retlen = len[i];
        }
        return retcount;
    }
};

运行结果:
在这里插入图片描述

4.最长数对链

题目链接
题目:

在这里插入图片描述

样例 输出和输入:

在这里插入图片描述

这道题其实和求最长子序列的长度是相同的题,但是换了一个形式而已,根据题目条件我们可以得知什么是数对链:
在这里插入图片描述
数对连就要满足上述条件
算法原理:
预处理:首先我们得将数组排序,排序的规则,只需要比较每个数对的第一个元素的大小即可,因为每个数对都是单增的,如果我们排序之后保证了a>c,那么d>c是绝对大于前一个数对的,所以这里只需要根据前一个数排序即可。
状态表示:这里dp[i]表示以i位置为结尾的所有数对链中最长的那个数对链的长度。
状态转移方程:分两种情况:
在这里插入图片描述

代码展示:

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

运行结果:
在这里插入图片描述

5.最长定差子序列

题目链接
题目:

在这里插入图片描述

样例 输出和输入:

在这里插入图片描述

这道题给定一个difference,让我们求出数组中的差为difference的最长的子序列的长度
算法原理:
状态表示:dp[i]表示以i位置为结尾的所有子序列中的最长的等差子序列,且差值是difference。
状态转移方程:首先我们可以分析一下,我们可以选择从0位置开始遍历寻找和i位置之差是difference的数,这里的dp表其实我们可以借助hash表来充当,因为每次我们都得去前面找和i位置差值是difference的数,所以这里hash表既可以充当dp表,也可以将前一个位置和当前位置的差值是difference的数存起来。
这里的状态转移方程:hash[arr[i]] = hash[arr[i] - difference] + 1这里如果没有在hash表中找到前一个位置差值是difference值的数,则hash[arr[i] - difference]就是0,所以也免去了这种情况,由于我们找的是离i位置最近的前一个位置,这里也可以用hash表解决,因为,我们是从左到右遍历的,这就使得后一个位置每次都是覆盖了前一个位置的值,每次都是最新的状态值。

代码展示:

class Solution {
public:
    int longestSubsequence(vector<int>& arr, int difference) 
    {
        unordered_map<int, int> hash;//arr[i]----dp[i]
        hash[arr[0]] = 1;
        int ret = 1;
        for (int i = 1;i < arr.size();i++)
        {
            //需要的最后一个b的值,这个hash能保证,因为从左到右遍历,前面的值已经被覆盖了
            hash[arr[i]] = hash[arr[i] - difference] + 1;
            ret = max(ret, hash[arr[i]]);
        }
        return ret;
        
    }
};

运行结果:

在这里插入图片描述

总结

通过本文对子序列问题的探讨,我们深入理解了动态规划在解决此类问题中的重要性。无论是经典的最长公共子序列(LCS)问题,还是最长递增子序列(LIS)问题,动态规划都展示了其强大的解题能力。通过将问题分解为更小的子问题,并记录这些子问题的解,我们能够高效地找到最优解,避免重复计算。

此外,我们还见识了动态规划解决子序列问题的多种变体及其实际应用。这不仅拓宽了我们对算法设计的视野,也提升了我们在面对复杂问题时的解决能力。子序列问题不仅在理论上具有重要意义,也在现实世界中的许多领域,如生物信息学、文本处理和数据分析中有着广泛的应用。

希望通过本文的讲解,读者能对动态规划在子序列问题中的应用有更深的理解,并能将这些技术应用于实际编程中,解决更多实际问题。动态规划的学习不仅仅局限于特定问题,更是培养一种思维方式,一种解决复杂问题的系统方法。愿大家在未来的算法学习和应用中继续精进,取得更大的进步。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【2024德国签证】去德国读博士需要申请什么签证?

德国留学签证面签的经过及注意事项 ✨&#xff01;希望我的经验可以帮助大家顺利通过签证&#xff0c;顺利开启德国留学之旅 。记得带上足够的现金和材料哦 &#xff01; 一、选择适合自己的签证类型 在选择签证类型时&#xff0c;一定要根据自己的实际情况来选择合适的签证种…

Wp-scan一键扫描wordpress网页(KALI工具系列三十二)

目录 1、KALI LINUX 简介 2、Wp-scan工具简介 3、信息收集 3.1 目标IP&#xff08;服务器) 3.2kali的IP 4、操作实例 4.1 基本扫描 4.2 扫描已知漏洞 4.3 扫描目标主题 4.4 列出用户 4.5 输出扫描文件 4.6 输出详细结果 5、总结 1、KALI LINUX 简介 Kali Linux 是一…

JAVA极简图书管理系统,初识springboot后端项目

前提条件&#xff1a; 具备基础的springboot 知识 Java基础 废话不多说&#xff01; 创建项目 配置所需环境 将application.properties>application.yml 配置以下环境 数据库连接MySQL 自己创建的数据库名称为book_test server:port: 8080 spring:datasource:url:…

存储故障导致Oracle 19c 数据文件处于recover状态的恢复案例

1.背景 某次平台分布式存储故障&#xff0c;导致数据库出现ORA-00376、ORA-01110数据文件不可读报错&#xff0c;本文将整个恢复过程进行整理记录。 2.报错信息 在进行租户数据库打开操作时&#xff0c;出现了如下报错&#xff1a; ORA-00376: file 17 cannot be read at t…

DICOM灰度图像、彩色图像的窗宽、窗位与像素的最大最小值的换算关系?

图像可以调整窗宽、窗位 dicom图像中灰度图像可以调整窗宽、窗位&#xff0c;RGB图像调整亮度或对比度&#xff1f;_灰度 图 调节窗宽-CSDN博客 窗宽、窗位与像素的最大最小值的换算关系? 换算公式 max-minWindowWidth; (maxmin)/2WindowCenter; 详细解释 窗宽&#xff0…

谷歌重磅:告别RAG,长上下文的大语言模型无需检索增强

当今人工智能领域正在经历一场静默的革命。随着大语言模型(LLM)的快速发展&#xff0c;它们不仅能够处理更长的上下文&#xff0c;还展现出惊人的推理和检索能力。 难道我们要告别基于LLM的检索增强生成(RAG)了吗&#xff1f; 结果还真是这样&#xff0c;最近谷歌发布专门用于…

贪心算法算法,完全零基础小白教程,不是计算机的都能学会!超详解

目录 一、基本概念 二、举几个例子&#xff0c;便于理解 1、找零问题 2、最小路径和 3、背包问题 1&#xff09;只考虑体积的贪心策略&#xff1a; 2&#xff09; 只考虑价值的贪心策略&#xff1a; 三、贪心策略的特点 四、贪心策略证明 四、如何学习贪心 五、例题…

eNSP中WLAN的配置和使用

一、基础配置 1.拓扑图 2.VLAN和IP配置 a.R1 <Huawei>system-view [Huawei]sysname R1 GigabitEthernet 0/0/0 [R1-GigabitEthernet0/0/0]ip address 200.200.200.200 24 b.S1 <Huawei>system-view [Huawei]sysname S1 [S1]vlan 100 [S1-vlan100]vlan 1…

IAR工程目录移动报错(改变文件目录结构)

刚开始用IAR&#xff0c;记录一下。 工作中使用华大单片机&#xff0c;例程的文件目录结构太复杂了想精简一点。 1.如果原本的C文件相对工程文件&#xff08;.eww文件&#xff09;路径变化了&#xff0c;需要先打开工程&#xff0c;再将所有的.c文件右键Add添加进工程&#xf…

PHP7源码结构

PHP7程序的执行过程 1.PHP代码经过词法分析转换为有意义的Token&#xff1b; 2.Token经过语法分析生成AST&#xff08;Abstract Synstract Syntax Tree&#xff0c;抽象语法树&#xff09;&#xff1b; 3.AST生成对应的opcode&#xff0c;被虚拟机执行。 源码结构&#xff1…

昇思25天学习打卡营第14天|CycleGAN图像风格迁移互换

模型介绍 模型简介 CycleGAN(Cycle Generative Adversarial Network) 即循环对抗生成网络&#xff0c;该模型实现了一种在没有配对示例的情况下学习将图像从源域 X 转换到目标域 Y 的方法。 该模型一个重要应用领域是域迁移&#xff0c;它只需要两种域的数据&#xff0c;而不…

2023-2024华为ICT大赛中国区 实践赛网络赛道 全国总决赛 理论部分真题

Part1 数通模块(10题)&#xff1a; 1、如图所示&#xff0c;某园区部署了IPv6进行业务测试&#xff0c;该网络中有4台路由器&#xff0c;运行OSPFv3实现网络的互联互通&#xff0c;以下关于该OSPFv3网络产生的LSA的描述&#xff0c;错误的是哪一项?(单选题) A.R1的LSDB中将存在…

Java高级重点知识点-13-数据结构、List集合、List集合的子类

文章目录 数据结构List集合List的子类&#xff08;ArrayList集、LinkedList集&#xff09; 数据结构 栈 stack,又称堆栈&#xff0c;它是运算受限的线性表&#xff0c;其限制是仅允许在标的一端进行插入和删除操作&#xff0c;不允许在其他任何位置进行添加、查找、删除等操作…

如何下载huggingface仓库里某一个文件

如何下载huggingface仓库里某一个文件&#xff1a; https://huggingface.co/PixArt-alpha/PixArt-Sigma/tree/main 直接用命令&#xff1a; wget https://huggingface.co/PixArt-alpha/PixArt-Sigma/resolve/main/PixArt-Sigma-XL-2-2K-MS.pth

30个!2024重大科学问题、工程技术难题和产业技术问题发布

【SciencePub学术】中国科协自2018年开始&#xff0c;组织开展重大科技问题难题征集发布活动&#xff0c;引导广大科技工作者紧跟世界科技发展大势&#xff0c;聚焦国家重大需求&#xff0c;开展原创性、引领性研究&#xff0c;不断夯实高质量发展的科技支撑。 自2024年征集活动…

南京林业大学点云相关团队论文

【1】Chen Dong, Wan Lincheng, Hu Fan, Li Jing, Chen Yanming, Shen Yueqian*, Peethambaran Jiju, 2024. Semantic-aware room-level indoor modeling from point clouds, International Journal of Applied Earth Observation and Geoinformation, 2024, 127, 103685. 语义…

QT5 static_cast实现显示类型转换

QT5 static_cast实现显示类型转换&#xff0c;解决信号重载情况

一款十六进制编辑器,你的瑞士军刀!!【送源码】

软件介绍 ImHex是一款功能强大的十六进制编辑器&#xff0c;专为逆向工程师、程序员以及夜间工作的用户设计。它不仅提供了基础的二进制数据编辑功能&#xff0c;还集成了一系列高级特性&#xff0c;使其成为分析和修改二进制文件的理想工具。 功能特点 专为逆向工程、编程和夜…

【AI】Image Inpainting

学习参考摘抄来自&#xff1a;大模型修复徐克经典武侠片&#xff0c;「全损画质」变4K&#xff0c;还原林青霞40年前绝世美貌 火山引擎多媒体实验室 &#xff08;1&#xff09;清晰度 去噪、去压缩、去模糊、超分辨率、人像增强 &#xff08;2&#xff09;流畅度 智能插帧算…

3.js - 纹理的重复、偏移、修改中心点、旋转

你瞅啥 上字母 // ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls // 导入lil.gui import { GUI } from three/examples/jsm/libs/lil-gui.module.min.js // 导入twee…