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

💕"深思熟虑的结果往往就是说不清楚。"💕
作者: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;
    }
}

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

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

相关文章

LeetCode 热题 HOT 100(P21~P30)

系列文章&#xff1a; LeetCode 热题 HOT 100(P1~P10)-CSDN博客 LeetCode 热题 HOT 100(P11~P20)-CSDN博客 LeetCode 热题 HOT 100(P21~P30)-CSDN博客 LC48rotate_image . - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 给定一个 n n 的二维矩阵 matrix 表…

Practical Network Acceleration with Tiny Sets

文章目录 why-AbstractIntroductionContributionsRelated WorksFilter-level pruningBlock-level pruningData Limited Knowledge DistillationMethodOverviewThe motivation to drop blocksThe recoverability of the pruned modelRecover the accuracy of the pruned modelEx…

力扣:205. 同构字符串

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那么这两个字符串是同构的。 每个出现的字符都应当映射到另一个字符&#xff0c;同时不改变字符…

Leetcode——560. 和为 K 的子数组

560. 和为 K 的子数组 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/subarray-sum-equals-k/description/ 题目描述&#xff1a; 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回该数组中和为 k 的子数组的个数 。子数组是数组中元素…

ABeam德硕|中国与柴火创客空间达成战略合作,拟定联合发布企业数字化转型实战课程

引言 随着近年数字技术的迅速发展&#xff0c;企业纷纷寻求数字化转型&#xff0c;而数字化转型企业人才的培养正是其中的关键一环。数字化转型人才能够从战略层面把握转型方向&#xff0c;快速适应新技术变革&#xff0c;有效应用技术工具以优化业务流程、提高组织效率、实践创…

如何配置元数据?(如何使用Spring容器)

目录 一、引出问题&#xff08;如何配置元数据&#xff1f;&#xff09;二、没有Spring的时代三、XML配置文件&#xff08;xml配bean&#xff09;1 格式1.1 示例 2 实例化一个Spring容器3 使用Spring容器4 后言 四、基于注解的配置 【[1.9. Annotation-based Container Configu…

代码随想录算法训练营第五十四天|392.判断子序列、115.不同的子序列

392.判断子序列 刷题https://leetcode.cn/problems/is-subsequence/description/文章讲解https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html视频讲解https://www.bilibili.com/video/BV1tv4y1B7ym/?vd_sourceaf4853e80f89e28094a5fe1e220…

Maven构建项目时,发生依赖下载错误的情况

在使用 Maven 构建项目时&#xff0c;可能会发生依赖下载错误的情况&#xff0c;主要原因有以下几种 1、下载依赖时&#xff0c;出现网络故障&#xff0c;或仓库服务器宕机等原因&#xff0c;导致无法连接至Maven仓库(也就是我们配置的阿里镜像)&#xff0c;从而无法下载依赖 …

国内git最新版本下载链接2.44

git官网地址:Git - Downloading Package (git-scm.com) 蓝奏云: ​​​​​​gGit-2.44.0-64-bit.exe - 蓝奏云 git仓库地址:git/git: Git Source Code Mirror - This is a publish-only repository but pull requests can be turned into patches to the mailing list via …

GPU从虚拟化迈向池化:趋动OrionX产品的创新之路

/ 引言 / 随着人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;技术的飞速发展&#xff0c;图形处理单元&#xff08;GPU&#xff09;已成为数据中心和云计算环境中的关键资源。GPU的并行处理能力使其成为执行复杂计算任务的理想选择。 然而&#xff…

Docker进阶:Docker-compose 实现服务弹性伸缩

Docker进阶&#xff1a;Docker-compose 实现服务弹性伸缩 一、Docker Compose基础概念1.1 Docker Compose简介1.2 Docker Compose文件结构 二、弹性伸缩的原理和实现步骤2.1 弹性伸缩原理2.2 实现步骤 三、技术实践案例3.1 场景描述3.2 配置Docker Compose文件3.3 使用 docker-…

【vue3学习之路(一)】

文章目录 前言一、vue3项目创建1.1环境准备1.1.1 基于 vue-cli 创建&#xff08;脚手架创建&#xff09;1.1.2 基于 vite 创建&#xff08;推荐&#xff09; 二、熟悉流程总结 前言 参考视频&#xff1a;https://www.bilibili.com/video/BV1Za4y1r7KE?p10&spm_id_frompag…

EFcore的实体类配置

1 约定配置 约定大于配置&#xff0c;框架默认了许多实体类配置的规则&#xff0c;在约定规则不满足要求时&#xff0c;可以显示地定义规则 1 数据库表明在不指定的情况下&#xff0c;默认使用的是数据库上下文类【DBContext】中DbSet 的属性名&#xff1b; 2 数据库表列的名字…

Vue3新手教程

Vue3新手教程 一. Vue3简介1. 性能的提升2.源码的升级3. 拥抱TypeScript4. 新的特性 二. 创建Vue3工程1. 基于 vue-cli 创建2. 基于 vite 创建(推荐)3. 一个简单的效果 三. Vue3核心语法1. OptionsAPI 与 CompositionAPI2. 拉开序幕的 setup2.1 setup 概述2.2 setup 的返回值2.…

【计算机考研】 跨考408全年复习规划+资料分享

跨专业备考计算机考研408&#xff0c;确实是一项挑战。在有限的时间内&#xff0c;我们需要合理安排时间&#xff0c;制定有效的学习计划&#xff0c;做到有效地备考。回顾我之前对408的经验&#xff0c;我想分享一些备考计划和方法。 要认清自己的起点。作为跨专业考生&#…

智能计算模拟: DFT+MD+ML 深度融合及科研实践

第一性原理、分子动力学与机器学习三者的交汇融合已在相关研究领域展现强劲的研究热潮。借助第一性原理计算揭示材料内在的量子特性&#xff0c;并结合分子动力学模拟探究材料在实际环境下的动态行为&#xff1b;运用机器学习算法与上述方法结合&#xff0c;开发高性能预测模型…

GaussDB WDR分析之节点篇与点评分析

今天继续介绍GaussDB的WDR报告&#xff0c;我们今天分析一下CN/DN节点的报告。昨天分析集群报告的时候发现集群报告里缺乏一些DBA分析问题所需要的数据&#xff0c;今天我们来看看是否在节点的报告里能够找到它们。GaussDB的节点报告格式都差不多&#xff0c;只不过CN/DN节点的…

力扣hot100:994. 腐烂的橘子(多源BFS)

这是一个典型的多源BFS问题&#xff0c;如果初学数据结构的同学&#xff0c;可能第一次不能想到&#xff0c;但是如果做过一次应该就能运用了。      主要思路大概是初始时&#xff0c;多个点进入队列然后进行BFS。将某一等价集合视作同一个起始点&#xff08;超级源点&…

阿里二面:Java中锁的分类有哪些?你能说全吗?

引言 在多线程并发编程场景中&#xff0c;锁作为一种至关重要的同步工具&#xff0c;承担着协调多个线程对共享资源访问秩序的任务。其核心作用在于确保在特定时间段内&#xff0c;仅有一个线程能够对资源进行访问或修改操作&#xff0c;从而有效地保护数据的完整性和一致性。…

kvm虚拟化

kvm虚拟化 1. 虚拟化介绍 虚拟化是云计算的基础。简单的说&#xff0c;虚拟化使得在一台物理的服务器上可以跑多台虚拟机&#xff0c;虚拟机共享物理机的 CPU、内存、IO 硬件资源&#xff0c;但逻辑上虚拟机之间是相互隔离的。 物理机我们一般称为宿主机&#xff08;Host&…