算法系列--动态规划--子序列(1)

news2024/9/23 20:12:35

💕"深思熟虑的结果往往就是说不清楚。"💕
作者:Mylvzi
文章主要内容:算法系列–动态规划–子序列(2)
在这里插入图片描述

今天带来的是算法系列--动态规划--子序列(1),是子序列问题的开篇!带大家初识子序列问题

一.什么是子序列问题

我们之前已经学习过子数组问题,子数组问题最大的特点就是求一段连续区间的xxxx,子数组问题的经典的状态表示就是以i位置为结束,xxxx,推导状态转移方程的一个经验是根据数组的结构来区分不同的结构

子序列问题本质上是对子数组问题的一个拓展,或者说子序列问题包含了子数组问题

在这里插入图片描述
子序列问题相较于子数组问题最大的不同在于序列可以是不连续的,也就是序列既可以连续又可以不连续,所以说子序列问题包含了子数组问题

但是子序列问题的状态表示和状态转移方程的推导方式和子数组问题十分相似!可以总结为以下步骤:

  1. 根据经验,确定状态表示dp[i],这一步和子数组问题中的状态表示很像,往往都是根据题目的意思+经验确定状态表示
  2. 状态转移方程:子序列/子数组问题的状态转移方程的推导方式比较固定,既按照构成子序列/子数组的形式确定,一般就分为两类
  • 单独一个 nums[i]
  • 前面一堆 + nums[i]
  1. 初始化:对于子序列问题来说,一般单独的一个数字也可以表示一种状态(1),1就表示最次状态,所以往往将dp表初始化为1
  2. 填表顺序:不固定,但一般都是从左往右的线性表
  3. 返回值:具体问题具体分析

下面是一些经典问题:

二.子序列题目讲解

1.最⻓递增⼦序列

链接: 最⻓递增⼦序列

分析:

  1. 状态表示:dp[i]表示以i为结束位置,最长的递增子序列的长度
  2. 状态转移方程:观察构成dp[i]的形式有两种,nums[i]单独一个,此时dp[i]==1,第二种形式是和之前的任意一段子序列重新构成一个新的递增子序列,设前面子序列的结束位置为j,则dp[i] =Max(dp[j] + 1)
  3. 初始化:由于一个nums[i]也可以组成一个子序列,所以最次的状态就是1,进而可以将dp表初始化为1(这样也就可以不讨论状态转移方程中nums[i]单独一个的情况)
  4. 填表顺序:从左至右
  5. 返回值:返回dp[i]的最大值

在这里插入图片描述

代码:

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];

        for(int i = 0 ; i < n; i++) dp[i] = 1;// 初始化dp表

        int ret = dp[0];// 记录最值

        // 填表
        for(int i = 1; i < n; i++) {
            for(int j = 0; j < i; j++) {
                if(nums[i] > nums[j])// 注意不是任何一个子序列都可以和nums[i]构成严格递增的子序列的  必须符合条件
                    dp[i] = Math.max(dp[j] + 1,dp[i]);
            }

            ret = ret > dp[i] ? ret : dp[i];
        }

        return ret;
    }
}

注意:

子序列问题相较于子数组问题的解题过程多了一层for循环,正是因为子序列问题的不连续的特性,所以要遍历i位置之前的所有子序列,时间复杂度相较于子数组问题也更高

2.摆动序列

链接:摆动序列

分析:

  1. 状态表示:根据经验很容易想到这道题的状态表示是以i位置为结束的摆动序列的最长子序列的的长度,但是在分析接下来的状态转移方程时,发现dp[i]的状态是收到前一个数的差值的正负所影响的,如果前面的差值是负,则nums[i]与前一序列结尾的数字nums[j]的差值必须为正,反之依然,所以仅仅通过一个状态转移方程是不够的,我们需要创建出两个状态转移方程表

    f[i]:以i位置为结尾,nums[i]与nums[j]的差值为的最长子序列的长度
    g[i]:以i位置为结尾,nums[i]与nums[j]的差值为的最长子序列的长度

2.状态转移方程:
在这里插入图片描述

  1. 初始化:最次状态为1,所以两个表都初始化为1
  2. 填表:从左往右
  3. 返回值:两个表的最大值

代码:

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;

        int[] f = new int[n];// 差值为正数的dp表
        int[] g = new int[n];// 差值为负数的dp表

        for(int i = 0; i < n; i++) f[i] = g[i] = 1;
        int ret = f[0];

        for(int i = 1; i < n; i++) {
            for(int j = 0; j < i; j++) {
                if(nums[i] - nums[j] > 0) f[i] = Math.max(g[j] + 1,f[i]);
                else if(nums[i] - nums[j] < 0) g[i] = Math.max(f[j] + 1,g[i]);
            }

            ret = Math.max(f[i],g[i]);// 更新最大值
        }

        return ret;
    }
}

3.最长定差子序列

链接:最长定差子序列

分析:

  • 笔者一拿到本题就想到了最长递增子序列那道题目,只不过这里的增加的条件变为了nums[i] - nums[j] == difference,但是大致的过程是相同的,笔者兴冲冲的C,V结果发现超时(小丑了)
  • 优化策略:超时代码中时间复杂度最高的地方在于需要对j从0位置一直遍历到i - 1,再加上外层循环,时间复杂度达到O(N^2),所以需要优化这里的寻找过程
  • 这个寻找过程的目的是找到最长的定差子序列,但是实际上没有必要从0开始找,如果有两个重复的nums[j],我们只需要取其中对应的dp[j]较大的值即可.而且,我们在遍历nums[i]的过程中,可以将每次遍历得到的nums[i]和dp[i]存入到哈希表之中,这样再回头去找最长的子序列时,只需寻找key = a - difference即可,这样搜索的时间复杂度就降低到O(1)

超时代码

class Solution {
    public int longestSubsequence(int[] nums, int difference) {
        int n = nums.length;
        int[] dp = new int[n];

        for(int i = 0 ; i < n; i++) dp[i] = 1;// 初始化dp表

        int ret = dp[0];// 记录最值

        // 填表
        for(int i = 1; i < n; i++) {
            for(int j = 0; j < i; j++) {
                if(nums[i] - nums[j] == difference)
                    dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1;
            }

            ret = ret > dp[i] ? ret : dp[i];
        }

        return ret;
    }
}

优化:

class Solution {
    public int longestSubsequence(int[] arr, int difference) {
        Map<Integer,Integer> hash = new HashMap<>();
        int ret = 1;// 记录最值

        for(int a : arr) {
            hash.put(a,hash.getOrDefault(a-difference, 0 ) + 1);// 将当前位置插入到哈希表中
            ret = Math.max(ret,hash.get(a));// 更新最值
        }

        return ret;
    }
}

4.最长数对链

链接:最长数对链

分析:

  • 本题其实和最长递增子序列很像,只不过这里换成了数对,需要注意的是,本题的子序列是可以任意挑选的,所以需要对数组进行排序

代码:

class Solution {
    public int findLongestChain(int[][] nums) {
        // 预处理数组 --转化为子序列问题
        Arrays.sort(nums,(a,b) -> {return a[0] - b[0];});// 此处使用的lambda表达式

        // 下面的思路和"最长递增子序列长度"相同
        int n = nums.length;
        int[] dp = new int[n];
        for(int i = 0; i < n; i++) {
            dp[i] = 1;
        }

        int ret = dp[0];
        for(int i = 1; i < n; i++) {
            for(int j = 0; j < i; j++) {
                if(nums[i][0] > nums[j][1]) {
                    dp[i] = Math.max(dp[i],dp[j] + 1);
                }
            }

            ret = ret > dp[i] ? ret : dp[i];
        }
    
        return ret;
    }
}

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

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

相关文章

【Burpsuite抓取APP、小程序数据包教程】

文章目录 一、抓取APP数据包1、burpsuite设置2、逍遥模拟器设置3、抓包即可 二、抓取小程序数据包1.bp设置代理端口2.利用火狐插件foxyproxy设置代理3.访问连接http://burp下载证书4.浏览器导入证书5.设置Proxifier6.配置代理规则7.进入小程序抓包 总结 一、抓取APP数据包 1、…

openGauss学习笔记-250 openGauss性能调优-使用Plan Hint进行调优-Join方式的Hint

文章目录 openGauss学习笔记-250 openGauss性能调优-使用Plan Hint进行调优-Join方式的Hint250.1 功能描述250.2 语法格式250.3 参数说明250.4 示例 openGauss学习笔记-250 openGauss性能调优-使用Plan Hint进行调优-Join方式的Hint 250.1 功能描述 指明Join使用的方法&#…

Intel被喷惨的大小核CPU终于有救,12、13代也沾了光

2021年 Intel 将混合架构引入 PC &#xff0c;至今也没能让所有用户接受这一改动。 虽然 PE 核心设计 帮助我们更好理解鸡兔同笼问题 带来了额外的多线程性能提升&#xff0c;但对于游戏玩家们可就不那么友好了。 关了吧觉得亏&#xff0c;不关吧又要时不时担心大核偷懒、小核…

【数据结构取经之路】队列循环队列

目录 引言 队列的性质 队列的基本操作 初始化 判空 销毁 队列的长度 插入 删除 返回队头元素 循环队列 假溢出 空与满的判定 实现 初始化 插入 判空 销毁 删除 返回队列长度 返回队列头元素 判满 引言 队列和栈一样&#xff0c;也是数据结构的一种&…

蓝桥杯基础练习详细解析一(代码实现、解题思路、Python)

试题 基础练习 数列排序 资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 给定一个长度为n的数列&#xff0c;将这个数列按从小到大的顺序排列。1<n<200 输入格式 第…

同城桶装水订水送水小程序:为水站打开新局面;

你是否还在为传统的电话、微信订水方式而烦恼&#xff1f;效率低下、管理混乱&#xff0c;是时候拥抱变革&#xff0c;让生意更上一层楼了&#xff01;同城送水小程序&#xff0c;一键解决你的所有痛点&#xff0c;让您的水站焕发新生。 水站系统功能亮点&#xff1a; 1. 一键…

无货源电商上货成功解析,如何选用采集和上货软件提高工作效力

最近大家是不是有一个疑惑&#xff1f;就是咱们在上货的过程中会出现解析失败 不论怎么上传&#xff0c;数据都是失败的&#xff0c;上传成功率很低很低&#xff0c;换了各种的上货软件 几百的几千的软件都用过了效果还是不行&#xff1f;和代理宣传的不符&#xff1f;什么日传…

如何成功将自己开发的APP上架到应用商店

如何成功将自己开发的APP上架到应用商店 随着移动应用市场的蓬勃发展&#xff0c;开发一款优秀的APP已成为许多企业和个人的首要选择。然而&#xff0c;成功上架并有效推广APP至关重要。本文将逐步介绍完整的上架流程&#xff0c;包括准备所需材料、注册开发者账户、进行APP备…

【Python实战】——神经网络识别手写数字

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

学习Python的第二天

下载工具 PyCharm Community Edition 2023.3.4 下载环境 Python3.10.4 目录 1.初识python 1.1 Python的起源 1.2 为什么学习Python 2.什么是编程语言 2.1 语言的概念 2.2 为什么不使用中文来与计算机交流呢 3.python环境安装 4.第一个python程序 5.python解释器 5…

人才推荐 | 纺织科学与工程博士,经验丰富的面料专家和采购专家

编辑 / 木子 审核 / 朝阳 伟骅英才 伟骅英才致力于以大数据、区块链、AI人工智能等前沿技术打造开放的人力资本生态&#xff0c;用科技解决职业领域问题&#xff0c;提升行业数字化服务水平&#xff0c;提供创新型的产业与人才一体化服务的人力资源解决方案和示范平台&#x…

一款炫酷的python形状绘制动画库

这个库让复杂数学概念的可视化变得既简单又有趣&#xff0c;无论是线性代数、微积分&#xff0c;还是更高级的数学主题&#xff0c;Manim都能让它们栩栩如生&#xff0c;特别适合于制作数学视频和演示文稿。 特点 动画生成&#xff1a; Manim库提供了一套丰富的工具和方法&…

C++ unordered_set和unordered_map

哈希 1. unordered_set/unordered_map1.1 背景1.2 unordered_set1.2.1 特性1.2.2 常用方法 1.3 unordered_map1.3.1 特性1.3.2 常用方法 2. 哈希2.1概念2.2 哈希冲突2.2.1哈希函数2.2.2 解决哈希冲突2.2.2.1 闭散列2.2.2.2 开散列 1. unordered_set/unordered_map 1.1 背景 之…

新台阶——蓝桥杯单片机省赛第十四届程序设计题目

在做十四届题目之前&#xff0c;常常听学长说&#xff0c;十四届以前拿省一真的是右手就行&#xff0c;并不相信&#xff0c;在经历十四届痛苦的大量修bug和优化之后&#xff0c;或许学长的话真说对了几分。话不多说&#xff0c;我们开始一起完成单片机第十四届程序设计题目。 …

【】(综合练习)博客系统

在之前的学些中&#xff0c;我们掌握了Spring框架和MyBatis的基本使用&#xff0c;接下来 我们就要结合之前我们所学的知识&#xff0c;做出一个项目出来 1.前期准备 当我们接触到一个项目时&#xff0c;我们需要对其作出准备&#xff0c;那么正规的准备是怎么样的呢 1.了解需求…

基于傅里叶描述子的手势动作识别,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

ubuntu22.04物理机双系统手动分区

ubuntu22.04物理机双系统手动分区 文章目录 ubuntu22.04物理机双系统手动分区1. EFI系统分区2. 交换分区3. /根分区4. /home分区分区后的信息 手动分区顺序&#xff1a;EFI系统分区(/boot/efi)、交换分区(/swap)、/根分区、/home分区。 具体参数设置&#xff1a; 1. EFI系统分…

02. 【Android教程】开发环境搭建

在学习 Android 应用开发之前&#xff0c;我们先要完成环境的搭建&#xff0c;它将帮助我们将 Java 代码编译打包生成最终的 Android 安装包。本教程在 Mac 下完成安装&#xff0c;Windows 和 Linux 步骤类似&#xff0c;不同之处会着重区分。 1. 文件清单 Java SE Developmen…

JVM的知识

什么是JVM 1.JVM&#xff1a; JVM其实就是运行在 操作系统之上的一个特殊的软件。 2.JVM的内部结构&#xff1a; &#xff08;1&#xff09;因为栈会将执行的程序弹出栈。 &#xff08;2&#xff09;垃圾99%的都是在堆和方法区中产生的。 类加载器&#xff1a;加载class文件。…

芯片中小公司ERP系统的业务流程:揭秘数字化管理的新篇章

随着信息技术的飞速发展&#xff0c;ERP(企业资源规划)系统已成为众多企业实现数字化管理的重要工具。对于芯片中小公司而言&#xff0c;ERP系统更是提升运营效率、优化资源配置的关键所在。那么&#xff0c;芯片中小公司的ERP系统究竟是如何运作的呢?让我们一同揭开其业务流程…