【动态规划四】子序列问题

news2024/11/27 18:35:28

目录

leetcode题目

一、最长递增子序列

二、摆动序列

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

四、最长数对链

五、最长定差子序列

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

七、最长等差数列

八、等差数列划分 II


leetcode题目

一、最长递增子序列

300. 最长递增子序列 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-increasing-subsequence/1.题目解析

子序列与子数组的区别在于:子序列可以不连续,但是要求元素顺序与原数组元素顺序不变~

因此子序列本质是包含子数组的~, 本题要求求出最长的严格递增子序列的长度

2.算法分析

1.状态表示

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

2.状态转移方程

3.初始化
dp表全部初始化成1

4.填表顺序

从左向右

5.返回值

返回dp表的最大值

3.算法代码

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) 
    {
        //1.创建dp表
        int n = nums.size();
        vector<int> dp(n, 1);
        //2.填表 + 返回值
        int ret = 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[i], dp[j] + 1);  
            }         
            ret = max(ret, dp[i]);
        }
        return ret;
    }
};

二、摆动序列

376. 摆动序列 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/wiggle-subsequence/description/

1.题目解析

摆动序列是指相邻元素之间的差值正负交替,只有1个元素或2个元素也叫做摆动序列,求数组中满足摆动序列的最长子序列

2.算法分析

1.状态表示

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

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

2.状态转移方程

3.初始化

两个表都初始化成1

4.填表顺序

从左向右两个表一起填

5.返回值

两个表的最大值

3.算法代码

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums)
    {
        //1.创建dp表
        int n = nums.size();
        vector<int> f(n, 1), g(n, 1);
        //2.填表 + 返回值
        int ret = 1;
        for(int i = 1; i < n; i++)
        {
            for(int j = 0; j < i; j++)
            {
                if(nums[i] > nums[j])  
                    f[i] = max(g[j] + 1, f[i]);
                if(nums[i] < nums[j])
                    g[i] = max(f[j] + 1, g[j]);
            }
            ret = max(ret, max(f[i], g[i]));
        }
        return ret;
    }
};

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

673. 最长递增子序列的个数 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/number-of-longest-increasing-subsequence/1.题目解析

求最长递增子序列的个数(严格递增)

2.算法分析

补充知识: 在数组中找出最大值出现的次数(要求遍历一次)

maxval = arr[0], count = 1, 遍历到数组中的元素x

1. x == maxval: count += 1

2. x < maxval: 无视

3. x > maxval: maxval = x, count = 0

1.状态表示

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

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

2.状态转移方程(参考上面的补充知识)

len[i] = count[i] = 1

j在[0, i-1]遍历, 在nums[j] < nums[i]的前提下:

①len[j] + 1 == len[i]:count[i] += count[j]

②len[j] + 1 < len[i]:无视

③len[j]+1 > len[i]:len[i] = len[j] + 1, count[i] = count[j]

3.初始化

两个表都初始化成1

4.填表顺序

从左往右两个表一起填

5.返回值

遍历len表的同时找出最大值出现的次数

3.算法代码

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) 
    {
        //1.创建dp表
        int n = nums.size();
        vector<int> len(n, 1), count(n, 1);
        //2.填表 + 返回值
        int retlen = 1, retcount = 1;
        for(int i = 1; i < n; i++)
        {
            for(int j = 0; j < i; 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;
    }
};

四、最长数对链

646. 最长数对链 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/maximum-length-of-pair-chain/1.题目解析

给定一个数组,数组每个元素是一个数对[left, right], left 是严格 < right 的,[a, b], [c, d], [e, f]要能构成数对链,要求b < c, d < e,  求能构成的最长数对链的长度

2.算法分析

依旧是动态规划,但是在分析状态转移方程时,求dp[i]往往要依赖前一个dp[i-1]的值或者依赖后一个dp[i+1]的值,而本题分析到某一个数对时,无法保证要用到的上一个数对是在该数对的前面

比如 [[1, 2], [7, 8], [4, 5]]这个数组,最终形成的数对链中的[4, 5]就链到了[7, 8]的前面

因此我们需要先对数组排序,  而排序之后:

对于[a, b], [c, d],  d > c >= a, 推出 d > a, 因此[c, d]是不可能链到[a, b]后面的, 这样就可以得出状态转义移方程了~

1.状态表示

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

2.状态转移方程

3.初始化

dp表全部初始化成1

4.填表顺序

从左往右填表

5.返回值

dp表的最大值

3.算法代码

class Solution {
public:
    int findLongestChain(vector<vector<int>>& pairs) 
    {
        //排序
        sort(pairs.begin(), pairs.end());
        //1.创建dp表
        int n = pairs.size();
        vector<int> dp(n, 1);
        //2.填表 + 返回值
        int ret = 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[i], dp[j]+1);
            }
            ret = max(ret, dp[i]);
        }
        return ret;
    }
};

五、最长定差子序列

1218. 最长定差子序列 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-arithmetic-subsequence-of-given-difference/description/1.题目解析

给定一个数组和difference, 返回最长定差子序列(相邻元素之间差为difference)的长度

2.算法分析

1.状态表示

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

2.状态转移方程

3.初始化

hash[arr[0]] = 1

4.填表顺序

从左往右

5.返回值

dp表的最大值(哈希表的最大值)

3.算法代码

class Solution {
public:
    int longestSubsequence(vector<int>& arr, int difference) 
    {
        //1.创建dp表(哈希表)
        unordered_map<int, int> hash; //arr[i]-dp[i]
        //2.初始化
        hash[arr[0]] = 1;
        //3.填表 + 返回值
        int ret = 1;
        for(int i = 1; i < arr.size(); i++)
        {
            hash[arr[i]] = hash[arr[i] - difference] + 1; //这一行既保证了b不存在的时候,hash[i]是1, 也保证了b是最后一个b
            ret = max(ret, hash[arr[i]]);
        }
        return ret;
    }
};

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

873. 最长的斐波那契子序列的长度 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence/1.题目解析

给定一个数组,求满足斐波那契式的最长的子序列的长度

2.算法分析

1.状态表示

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

ps:开始是定义的dp[i], 但是推不出状态转移方程, 因为只知道子序列的个数,但是无法确定具体的斐波那契式的子序列,也就无法根据dp[i]前面的值推导出dp[i]~

题目七与题目八的状态表示也是同样的得到方法~

2.状态转移方程

3.初始化

把dp表中的所有值都初始化成2(只会用到dp表中 i < j 的位置)

4.填表顺序

dp[i][j] = dp[k][i] + 1,  k < i && i < j, 因此从上往下填表即可

5.返回值

ret < 3 ? 0 : dp表中的最大值

3.算法代码

class Solution 
{
public:
    int lenLongestFibSubseq(vector<int>& arr) 
    {
        //1.将元素和下标绑定丢进哈希表
        int n = arr.size();
        unordered_map<int, int> hash;
        for(int i = 0; i < n; i++)
            hash[arr[i]] = i;
        //2.创建dp表
        vector<vector<int>> dp(n, vector<int>(n, 2));
        //3.填表+返回值
        int ret = 2;
        for(int j = 2; j < n; j++) //最后一个位置
        {
            for(int i = 1; i < j; i++) //倒数第二个位置
            {
                int a = arr[j] - arr[i];
                if(hash.count(a) && hash[a] < i) 
                    dp[i][j] = dp[hash[a]][i] + 1;
                ret = max(ret, dp[i][j]);
            }
        }
        return ret < 3 ? 0 : ret;
    }
};

七、最长等差数列

1027. 最长等差数列 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-arithmetic-subsequence/description/1.题目解析

给你一个整数数组 nums,返回 nums 中最长等差子序列的长度

2.算法分析

1.状态表示

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

2.状态转移方程

3.初始化

把dp表中的所有值都初始化成2(只会用到dp表中 i < j 的位置)

4.填表顺序

先固定倒数第2个数(b),再枚举倒数第一个数(c) --- 因为优化中,我们要保存的的是离i位置最近的a元素的下标,因此选择把b固定住,让c去移动

5.返回值

dp表中的最大值

3.算法代码

class Solution {
public:
    int longestArithSeqLength(vector<int>& nums) 
    {
        //优化
        unordered_map<int, int> hash;
        hash[nums[0]] = 0;

        //1.创建dp表 + 初始化
        int n = nums.size();
        vector<vector<int>> dp(n, vector<int>(n, 2));
        //2.填表 + 返回值
        int ret = 2;
        for(int i = 1; i < n; i++) //固定倒数第2个数
        {
            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(ret, dp[i][j]);
            }
            hash[nums[i]] = i; //i位置固定完之后,将nums[i]与i存入哈希表
        }
        return ret;
    }
};

八、等差数列划分 II

446. 等差数列划分 II - 子序列 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/arithmetic-slices-ii-subsequence/1.题目解析

求数组中等差子序列的个数(本题的等差子序列至少要有3个元素)

2.算法分析

1.状态表示

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

2.状态转移方程

3.初始化

dp表所有的值都初始化0(因为最坏情况下,两个元素是无法构成等差子序列的)

4.填表顺序

固定倒数第1个数,枚举倒数第2个数

5.返回值

dp表所有元素的和

3.算法代码

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) 
    {
        //优化
        int n = nums.size();
        unordered_map<long long, vector<int>> hash;
        for(int i = 0; i < n; i++)
            hash[nums[i]].push_back(i); 

        //1.创建dp表 + 初始化
        vector<vector<int>> dp(n, vector<int>(n));
        //2.填表 + 返回值
        int sum = 0;
        for(int j = 2; j < n; j++) //固定倒数第1个数
        {
            for(int i = 1; i < j; i++) //枚举倒数第2个数
            {
                long long a = (long long)2 * nums[i] - nums[j];
                if(hash.count(a))
                {
                    for(auto k : hash[a])
                    {
                        if(k < i) 
                            dp[i][j] += dp[k][i] + 1;
                    }
                }
                sum += dp[i][j];
            }
        }
        return sum;
    }
};

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

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

相关文章

关于SQL

数据库简介&#xff1a; 数据库分类 关系型数据库模型&#xff1a; 优点&#xff1a;易于维护&#xff0c;可以实现复杂的查询 缺点&#xff1a;海量数据 读取写入性能差&#xff0c;高并发下数据库的io是瓶颈 是把复杂的数据结构归结为简单的二元关系&#xff08;即二维表…

Linux字符设备驱动设计

Linux字符设备驱动设计 概述 驱动的定义与功能 计算机系统中存在着大量的设备&#xff0c; 操作系统要求能够控制和管理这些硬件&#xff0c; 而驱动就是帮助操作系统完成这个任务。 驱动相当于硬件的接口&#xff0c; 它直接操作、 控制着我们的硬件&#xff0c; 操作系统通…

工作太闲,平常有没有用手机能赚钱的,这里我推荐了4种可月入2000的副业

确实有许多通过手机赚钱的方式&#xff0c;以下是一些常见的方法 1.拍照卖图 如果你有一台相机或智能手机&#xff0c;喜欢拍照&#xff0c;那么可以将自己拍摄的图片上传到网站上&#xff0c;赚取稿费。 2. 做问卷调查 许多公司需要了解消费者的意见&#xff0c;所以会通过…

【XSRP软件无线电】基于软件无线电平台的QPSK频带通信系统设计

目录&#xff1a; 目录&#xff1a; 一、绪论 1.1 设计背景 1.2 设计目的 二、系统总体方案 2.1 专题调研题目 2.2 调研背景 2.3 设计任务解读 2.4 设计原理 2.4.1 原理框图 2.4.2 功能验证 三、软件设计 3.1 程序解读 3.2 程序设计 3.3 仿真结果&#xff1a; 四、程序代码分析…

爬取深圳2024年链家二手房数据,共3000条数据(其他城市也可)

文章目录 专栏导读1.目标2.导入相关库3.获取每个二手房的链接4.获取每个链接中的相关数据5.保存数据6.数据展示 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫…

二叉树OJ刷题

制作不易&#xff0c;三连支持一下吧&#xff01;&#xff01;&#xff01; 文章目录 前言一、相同的树二、单值二叉树三.对称二叉树四.二叉树的前序遍历五.另一棵树的子树六.二叉树遍历总结 前言 前三篇博客我们详细介绍了树形结构&#xff0c;及两种特殊的树&#xff1a;堆和…

【Ubuntu永久授权串口设备读取权限“/dev/ttyUSB0”】

Ubuntu永久授权串口设备读取权限 1 问题描述2 解决方案2.1 查看ttyUSB0权限&#xff0c;拥有者是root&#xff0c;所属用户组为dialout2.2 查看dialout用户组成员&#xff0c;如图所示&#xff0c;普通用户y不在dialout组中2.3 将普通用户y加入dialout组中2.4 再次查看dialout用…

云原生新手和开源教育分论坛 02-技术 or 非技术,参与 Kubernetes 社区丝滑路径【开源贡献】

https://www.kubernetes.dev/https://www.kubernetes.dev/community/community-groups/https://killercoda.com/https://kwok.sigs.k8s.io/https://training.linuxfoundation.cn/ 演讲

pywinauto,一款Win自动化利器!

pywinauto&#xff0c;一款Win自动化利器&#xff01; 1.安装 pywinauto是一个用于自动化Python模块&#xff0c;适合Windows系统的软件&#xff08;GUI&#xff09;&#xff0c;可以通过Pywinauto遍历窗口&#xff08;对话框&#xff09;和窗口里的控件&#xff0c;也可以控…

NeurIPS‘24 截稿日期逼近 加拿大温哥华邀你共赴盛会

会议之眼 快讯 第38届NeurIPS24(Conference and Workshop on Neural Information Processing Systems)即神经信息处理系统研讨会将于 2024 年 12月9日-15日在加拿大温哥华会议中心举行&#xff01; NeurIPS 每一年都是全球AI领域的一场盛宴&#xff0c;吸引着来自世界各地的顶…

软件文档-总体测试计划书(Word原件2024)

软件资料清单列表部分文档&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&#xff0c;用户需求说明书&#xff0c;概要设计说明书&#xff0c;技术解决…

第九期济南科创生态大会即将盛大开启,为中小企业注入新动能

【头部财经】近日&#xff0c;备受瞩目的第九期济南科创生态大会即将拉开帷幕。本次大会将聚焦科技创新领域&#xff0c;为参会者提供一个交流合作、资源对接的优质平台。 作为一个专为中小企业打造的科技创新交流平台&#xff0c;济南科创生态大会一直致力于促进中小企业的发…

【VTKExamples::Rendering】第七期 TestShadows

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例TestShadows,并解析接口vtkRenderer,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO …

解决Android手机无法通过蓝牙给win10 PC传送文件

&#xff08;一&#xff09;先配对设备&#xff0c;正常配对就可以 &#xff08;二&#xff09;打开系统设置&#xff0c;win搜索窗口搜索“设置” &#xff08;三&#xff09;搜索“蓝牙” &#xff08;四&#xff09;打开“蓝牙和其他设备”&#xff0c;点击“更多蓝牙设置”…

【2024年5月备考新增】】 考前篇(2)《官方平台 - 考生模拟练习平台常用操作(一)》

软考考生常用操作说明 说明:模拟作答系统是旨在让考生熟悉计算机化考试环境和作答方式,模拟作答不保存考生作答 历史记录。考试题型、题量、分值、界面及文字内容以正式考试答题系统为准。 1 如何标记试题、切换试题 2 简答题如何查看历史记录、切换输入法 3 选做题,已作答…

C++下使用Matplotlib-cpp的一些配置

Aconda3安装使用 https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/下载 切换清华源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ con…

自动化测试需知的4项测试工具

一般来说学自动化会建议大家先学selenium&#xff0c;因为最早的时候&#xff0c;自动化就代表selenium&#xff0c;进入测试行业就开始做接口测试&#xff0c;而且现在基本每个公司都需要接口测试。今天就和大家聊一下接口测试的工具。 一、Robot Framework 机器人框架。之所…

难以重现的 Bug如何处理

对很多测试人员&#xff08;尤其是对新手来说&#xff09;在工作过程中最不愿遇到的一件事情就是&#xff1a;在测试过 程中发现了一个问题&#xff0c;觉得是 bug&#xff0c;再试的时候又正常了。 碰到这样的事情&#xff0c;职业素养和测试人员长期养成的死磕的习性会让她…

04、 .java程序用 editplus 工具打开的过程及在 editplus 工具中配置 java/javac 命令的过程

EditPlus 工具的使用&#xff1a; 1、安装 editplus 工具的过程&#xff1a;其一、安装包地址&#xff1a;其二、安装步骤&#xff1a; 2、使用 editplus 工具打开 .java 程序的过程&#xff1a;其一、修改默认打开 .java 的工具&#xff1a;其二、效果展示&#xff1a; 3、在 …

电商平台接口自动化框架实践||电商API数据采集接口

电商数据采集接口 语言&#xff1a;python 接口自动化实现流程 红色为可实现/尚未完成 绿色为需要人工干预部分 自动生成测试用例模板&#xff08;俩种方式二选一&#xff09;&#xff1a; mimproxy&#xff0c;通过浏览器代理抓包方式&#xff0c;访问 H5 或者 web 页面&a…