动态规划算法专题(五):子序列问题

news2024/12/23 10:06:57

目录

1、最长递增子序列

1.1 算法原理

1.2 算法代码

2、摆动序列

2.1 算法原理

2.2 算法代码

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

3.1 算法原理

3.2 算法代码

4、最长数对链

4.1 算法原理

 4.2 算法代码

5、最长定差子序列

5.1 算法原理

5.2 算法代码

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

6.1 算法原理 

6.2 算法代码

7、最长等差数列

7.1 算法原理

7.2 算法代码

8、等差数列划分 II - 子序列(hard)

8.1 算法原理

8.2 算法代码


1、最长递增子序列

. - 力扣(LeetCode)

1.1 算法原理

  • 状态表示dp[i]:

以i位置为结尾的所有递增子序列中,最长递增子序列的长度

  • 状态转移方程:

dp[i]=max(dp[j]+1);// j 位于[0, i-1]区间 && nums[j] < nums[i] 

  • 初始化:

Arrays.fill(dp, 1);// 最差情况,本身

  • 建表顺序:

从左往右

  • 返回值:

dp表中的最大值 

1.2 算法代码

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        // 初始化(最差情况: 本身)
        Arrays.fill(dp, 1);
        int ret = 1;
        // 建表
        for(int i = 1; i < n; i++) {
            // j -> [0, i-1]
            for(int j = i - 1; j >= 0; j--) {
                if(nums[j] < nums[i]) {
                    dp[i] = Math.max(dp[j] + 1, dp[i]);
                }
            }
            ret = Math.max(ret, dp[i]);
        }
        return ret;
    }
}

2、摆动序列

. - 力扣(LeetCode)

2.1 算法原理

  • 状态表示:

f[i]: 以i位置为结尾,最后呈现 上升 趋势的最长摆动序列的长度
g[i]: 以i位置为结尾,最后呈现 下降 趋势的最长摆动序列的长度

  • 状态转移方程:

f[i] = max(g[j] + 1);

g[i] = max(f[j] + 1);

  • 初始化:

f表、g表中所有元素初始化为1

  • 建表顺序:

从左往右

  • 返回值:

g表和f表中的最大值

2.2 算法代码

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        // f[i]: 以i位置为结尾,最后呈现 上升 趋势的最长摆动序列的长度
        // g[i]: 以i位置为结尾,最后呈现 下降 趋势的最长摆动序列的长度
        int[] f = new int[n];
        int[] g = new int[n];
        // 初始化
        Arrays.fill(f, 1);
        Arrays.fill(g, 1);
        int ret = 1;
        // 填表
        for (int i = 1; i < n; i++) {
            // j -> [0, i-1]
            for(int j = i - 1; j >= 0; j--) {
                if(nums[j] > nums[i]) {
                    g[i] = Math.max(f[j] + 1, g[i]);
                }
                if(nums[j] < nums[i]) {
                    f[i] = Math.max(g[j] + 1, f[i]);
                }
            }
            ret = Math.max(ret, Math.max(g[i], f[i]));
        }
        return ret;
    }
}

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

. - 力扣(LeetCode)

3.1 算法原理

  • 状态表示:

count[i]:以i位置为结尾的所有子序列中,最长的递增子序列的 "个数"
len[i]:以i位置为结尾的所有子序列中,最长的递增子序列的 "长度"

  • 状态转移方程:

len[j]+1==len[i] --> count[i] += count[j]
len[j]+1 < len[i] --> 无视
len[j]+1 > len[i] --> count[i]=count[j] && len[i]=len[j]+1

  • 初始化:

两个表中的所有元素初始化为1(最差情况下)

  • 建表顺序:

从左往右

  • 返回值:

返回最长长度的递增子序列的"总个数"(求和)

3.2 算法代码

class Solution {
    public int findNumberOfLIS(int[] nums) {
        int n = nums.length;
        int[] len = new int[n];// 最长递增子序列的长度
        int[] count = new int[n];// 最长递增子序列的个数
        // 初始化
        Arrays.fill(len, 1);
        Arrays.fill(count, 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]) {
                    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]) retCount += count[i];
            else if(retLen < len[i]) {
                retLen = len[i];
                retCount = count[i];
            }
        }
        return retCount;
    }
}

4、最长数对链

. - 力扣(LeetCode)

4.1 算法原理

预处理:根据每个数对的第一个元素进行升序排序,使其满足动态规划顺序填表的规则

  • 状态表示:

dp[i]:以i位置为结尾的所有数对链中,最长数对链的 "长度"

  • 状态转移方程:

dp[i] --> j [0, i-1] --> pairs[j][1] < pairs[i][0] --> dp[i]=max(dp[j]+1 );

  • 初始化:

dp表中所有元素初始化为1

  • 建表顺序:

从左往右

  • 返回值:

dp表中的最大值

 4.2 算法代码

class Solution {
    public int findLongestChain(int[][] pairs) {
        // 预处理: 根据数对链的第一个元素进行升序排序 --> 满足动态规划顺序填表的要求
        Arrays.sort(pairs, (o1, o2) -> o1[0] - o2[0]);
        int n = pairs.length;
        int[] dp = new int[n];
        // 初始化
        Arrays.fill(dp, 1);
        int ret = 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] = Math.max(dp[i], dp[j] + 1);
                }
            }
            ret = Math.max(ret, dp[i]);
        }
        return ret;
    }
}

5、最长定差子序列

. - 力扣(LeetCode)

5.1 算法原理

  • 状态表示:

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

  • 状态转移方程:

b=a-difference;
从前面的序列中找最后一个b(假设为j位置) --> dp[i]=dp[j]+1;

优化:哈希表
①:将元素(arr[i])和其dp值绑定,放到哈希表中
②:不用创建dp表了,直接在哈希表中进行 动态规划

③:Map天然的去重特性

  • 初始化:

每个元素的dp值初始化为1(最差情况)

  • 建表顺序:

从左往右

  • 返回值:

哈希表中最大的dp值


5.2 算法代码

class Solution {
    public int longestSubsequence(int[] arr, int difference) {
        int n = arr.length;
        // <元素, 最长长度>
        Map<Integer, Integer> hash = new HashMap<>();
        int ret = 1;
        for(int i = 0; i < n; i++) {
            // 找上一个等差值
            int b = arr[i] - difference;
            int len = hash.getOrDefault(b, 0) + 1;
            hash.put(arr[i], len);
            ret = Math.max(ret, len);
        }
        return ret;
    }
}

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

. - 力扣(LeetCode)

6.1 算法原理 

  • 状态表示:

dp[i][j]:以i位置(前)以及j位置(后)元素结尾的所有子序列中,最长的斐波那契子序列的长度

  • 状态转移方程:

j位置元素为c,i位置元素为b,k位置元素为a,则a=c-b

1. a不存在 --> 2

2. a存在,但 i<k<j --> 2

3. a存在,且合法(k<i<j) --> dp[k][i]+1;

  • 建表顺序

从上往下

  • 返回值:

dp表中的最大值
处理特殊情况:return ret == 2 ? 0 : ret ;

6.2 算法代码

class Solution5 {
    public int lenLongestFibSubseq(int[] arr) {
        int n = arr.length;
        // <元素值, 下标>
        Map<Integer, Integer> hash = new HashMap<>();
        for(int i = 0; i < n; i++) hash.put(arr[i], i);
        int[][] dp = new int[n][n];
        // 初始化
        for(int i = 0; i < n; i++) Arrays.fill(dp[i], 2);
        int ret = 0;
        // 从上往下填表
        for(int j = 2; j < n; j++) {
            for(int i = 1; i < j; i++) {
                int x = arr[j] - arr[i];
                int index = hash.getOrDefault(x, -1);
                if(index != -1 && index < i) {
                    dp[i][j] = dp[index][i] + 1;
                    ret = Math.max(ret, dp[i][j]);
                }
            }
        }
        return ret;
    }
}

7、最长等差数列

. - 力扣(LeetCode)

7.1 算法原理

  • 状态表示:

dp[i][j]:以i位置(前)和j位置(后)为结尾的所有子序列中,最长的等差数列的长度

  • 状态转移方程:

dp[i][j] = dp[k][j] + 1;(k存在且合法)

  • 初始化:

dp表中所有元素初始化为2

  • 建表顺序:

从上往下

  • 返回值:

dp表中的最大值

优化:
边做dp,边将最近的元素及其下标, 存到哈希表中:固定i,枚举j       
          

7.2 算法代码

class Solution {
    public int longestArithSeqLength(int[] nums) {
        int n = nums.length;
        // dp[i][j]: 以(i, j)位置为结尾的所有子序列中,最长等差数列的长度
        int[][] dp = new int[n][n];
        // 优化 -> 将最近的元素放进哈希表中
        // <元素, 元素下标>
        Map<Integer, Integer> hash = new HashMap<>();
        hash.put(nums[0], 0);
        // 初始化
        for (int i = 0; i < n; i++) Arrays.fill(dp[i], 2);
        int ret = 2;
        // 建表(固定倒数第二个数, 枚举倒数第一个数) 
        for (int i = 1; i < n; i++) {
            for(int j = i + 1; j < n; j++) {
                int val = nums[i] * 2 - nums[j];
                int index = hash.getOrDefault(val, -1);
                if(index != -1) {
                    dp[i][j] = dp[index][i] + 1;
                    ret = Math.max(ret, dp[i][j]);
                }
            }
            hash.put(nums[i], i);
        }
        return ret;
    }
}

8、等差数列划分 II - 子序列(hard)

. - 力扣(LeetCode)

8.1 算法原理

  • 状态表示:

dp[i][j]:以i位置和j位置为结尾的所有子序列中,所有等差数列的个数

  • 状态转移方程:

a = 2*b-c

1. a不存在 --> dp[i][j] = 0
2. a存在,但不合法(k>=i) --> dp[i][j] = 0
3. a存在,且合法(k<i) --> dp[i][j] += dp[k][i]+1;(a可能重复存在,因为求的是个数,所以要全部计算)

  • 初始化:

dp表中所有值初始化为0

  • 建表顺序:

从上到下

  • 返回值:

dp表元素之和

优化:
dp前,将<元素,下标数组>绑定,存到哈希表中

8.2 算法代码

class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
        int n = nums.length;
        int[][] dp = new int[n][n];
        // <元素, 下标数组>
        // Long --> 防溢出
        Map<Long, List<Integer>> hash = new HashMap<>();
        for(int i = 0; i < n; i++) {
            List<Integer> list = hash.getOrDefault((long)nums[i], null);
            if(list != null) {
                list.add(i);
            }else {
                hash.put((long)nums[i], new ArrayList<>());
                hash.get((long)nums[i]).add(i);
            }
        }
        int ret = 0;
        for(int i = 1; i < n; i++) { // 固定倒数第二个数
            for(int j = i + 1; j < n; j++) { // 枚举倒数第一个数
                long val = 2L * nums[i] - nums[j]; // 找第一个数
                List<Integer> list = hash.getOrDefault(val, null);
                if(list != null) {
                    int sum = 0;
                    for(int x : list) {
                        if(x < i) sum += dp[x][i] + 1;
                    }
                    dp[i][j] = sum;
                    ret += sum;
                }
            }
        }
        return ret;
    }
}

END

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

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

相关文章

NASA:气象追踪分子光谱(ATMOS)二级产品,包含在垂直高度(公里)网格上的微量气体

目录 简介 摘要 引用 网址推荐 0代码在线构建地图应用 机器学习 ATMOS L2 Trace Gases on Altitude Grid, Tab Delimited Format V3 (ATMOSL2AT) at GES DISC 简介 这是版本3的气象追踪分子光谱&#xff08;ATMOS&#xff09;二级产品&#xff0c;包含在垂直高度&#…

多线程股吧(东方财富)用户信息爬取

多线程东方财富&#xff08;股吧&#xff09;用户信息爬取 在上一篇博客股吧信息爬取的基础上加入了多线程&#xff0c;使得速度提升了十几倍&#xff0c;爬取内容如下&#xff1a; 最终爬取结果如下&#xff1a; 完整代码如下&#xff08;准备好环境&#xff0c;比如pytho…

安宝特案例 | Fundació Puigvert 医院应用AR技术开创尿石症治疗新纪元

案例介绍 在医疗科技不断进步的今天&#xff0c;Fundaci Puigvert 医院迈出了重要一步&#xff0c;成功应用AR技术进行了全球首例同时使用两台内窥镜的ECIRS手术&#xff08;内镜肾内联合手术&#xff09;&#xff0c;由Esteban Emiliani M.D. PhD F.E.B.U 博士主刀。这标志着…

yub‘s Algorithmic Adventures_Day7

环形链表 link&#xff1a;https://leetcode.cn/problems/linked-list-cycle-ii/description/ 思路分析 我只能说双指针yyds【刻板hh】 我们分两种情况来分析 起码在第二圈才会相遇 fast比slow多走环的整数倍 fast 走的步数是 slow 步数的 2 倍&#xff0c;即 f2s&#xff…

5.资源《Arduino UNO R3 proteus 使用CD4511驱动数码管工程文件(含驱动代码)》说明。

资源链接&#xff1a; Arduino UNO R3 proteus 使用CD4511驱动数码管工程文件&#xff08;含驱动代码&#xff09; 1.文件明细&#xff1a; 2.文件内容说明 包含&#xff1a;proteus工程&#xff0c;内含设计图和工程代码。 3.内容展示 4.简述 工程功能可以看这个视频 数码…

微信小程序流量主

开发小程序也已经有一段时间了,也是为了添加流量主来开发小程序,根据小程序的定位,来获取用户想要的资源,通过广告的形式来增加用户的点击量进行收益,收益虽然微不足道,但是也是很有成就感的

活动邀请 | SonarQube×创实信息即将亮相2024 GOPS全球运维大会-上海站,分享代码质量与安全提升策略

2024年10月18日-19日&#xff08;周五-周六&#xff09;&#xff0c;第二十四届 GOPS 全球运维大会上海站将在上海中庚聚龙酒店举办。 大会为期2天&#xff0c;侧重大模型、DevOps、SRE、AIOps、BizDevOps、云原生及安全等热门技术领域。特设了如大模型 运维/研发测试、银行/…

宠物咖啡馆服务平台:SpringBoot技术深度解析

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于Spring Boot的宠物咖啡馆平台的设计与…

2024_10_8 系统进展

改进位置 发现是label_api里藏了我需要改进的东西 settings.py 数据库 我这边电脑上使用的是windows 192 vue.config.js 陈家强是这样设置的 module.exports {publicPath: process.env.NODE_ENV production? /: /,assetsDir: static,// css: {// extract: false// },…

使用XML实现MyBatis的基础操作

目录 前言 1.准备工作 1.1⽂件配置 1.2添加 mapper 接⼝ 2.增删改查操作 2.1增(Insert) 2.2删(Delete) 2.3改(Update) 2.4查(Select) 前言 接下来我们会使用的数据表如下&#xff1a; 对应的实体类为&#xff1a;UserInfoMapper 所有的准备工作都在如下文章。 MyBati…

《大规模语言模型从理论到实践》第一轮学习--Fine-tuning微调

第一轮学习目标&#xff1a;了解大模型理论体系 第二轮学习目标&#xff1a;进行具体实操进一步深入理解大模型 从大语言模型的训练过程来理解微调 大预言模型训练主要包含四个阶段&#xff1a;预训练、有监督微调、奖励建模、强化学习。 预训练&#xff08;Pretraining&…

[paddle]paddleseg快速开始

快速开始 为了让大家快速了解PaddleSeg&#xff0c;本文档使用一个简单示例进行演示。在实际业务中&#xff0c;建议大家根据实际情况进行调整适配。 在开始下面示例之前&#xff0c;请大家确保已经安装好PaddleSeg开发环境&#xff08;安装说明&#xff09;。 1 准备数据 …

被AI坑的一天—CentOS7导入阿里云YUM源报错的 GPG密钥提示404

过于相信人工智能 配置YUM源根据AI的说法换阿里云的YUM源验证AI配置结果解决问题 配置YUM源 由于电脑受限制 , 不能访问境外网站,所以用不了centos自带的源 ,是报404的 根据AI的说法换阿里云的YUM源 编辑 /etc/yum.repos.d/CentOS-Base.repo ,更换为 配置完成后sudo yum …

nacos多数据源插件介绍以及使用

概述 在微服务架构中&#xff0c;服务配置的集中管理和动态调整是至关重要的。Nacos 提供了配置管理和服务发现的功能&#xff0c;其中配置管理支持动态数据源的切换&#xff0c;增强了其在复杂环境中的适用性。默认情况下&#xff0c;Nacos 支持 MySQL 和Derby&#xff0c;但…

C++——AVL树的模拟实现

目录 一、AVL树结点 二、AVL树结构 三、插入数据&#xff08;重点&#xff09; 1、右单旋 2、左单旋 3、左右双旋 4、右左双旋 AVL树是一颗平衡二叉搜索树&#xff0c;它的本质就是一颗之前说过的二叉搜索树。但是二叉搜索树可能会出现极端情况&#xff0c;导致二叉搜索树变…

不同时期的USB接口

Type-A Type-A接口最早于USB1.0标准(1996)推出&#xff0c;拥有四个引脚&#xff1a;VBUS提供5V电源&#xff0c;D-和D用于数据传输&#xff0c;GND接地。 Type-B Type-B接口最早于USB1.0标准(1996)推出&#xff0c;拥有四个引脚&#xff1a;VBUS提供5V电源&#xff0c;D-和D用…

QD1-P7 HTML常用标签:div和span

本节学习&#xff1a;div 和 span 标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p7 ‍ 一、div 标签 用途 ​<div>​ 标签在 HTML 中是一个通用 容器 &#xff0c;用于将 HTML 文档中的内容分组并在文档中划分区域。<div> ​元素本身不具有特定的含…

道路积水检测数据集 1450张 路面积水 带分割 voc yolo

道路积水检测数据集 1450张 路面积水 带分割 voc yolo 分类名: (图片张数&#xff0c; 标注个数) puddle:(1468,1994) 总数:(1468&#xff0c;1994) 总类(nc): 1类 道路积水检测数据集介绍 项目名称 道路积水检测数据集 项目概述 本数据集包含1450张带有标注的图像&#x…

【ubuntu】ubuntu20.04安装cuda12.6与显卡驱动

目录 1.安装cuda12.6 2.安装显卡驱动 1.安装cuda12.6 https://developer.nvidia.com/cuda-toolkit-archive https://developer.nvidia.com/cuda-12-6-0-download-archive?target_osLinux&target_archx86_64&DistributionUbuntu&target_version20.04&target_…

记一次 stm32f407 无法进入 standby 问题

记一次 stm32f407 无法进入 standby 问题 通过查看当前中断信息,发现是 systick 中断pending未处理导致进入standby 模式的 WFI 失败,所以需要在执行 WFI 之前清除 systick 中断pending标志. 查看<Cortex M3与M4权威指南>如下: 可知ICSR寄存器的bit 26表示systick中断是…