【动态规划】C++ 子序列问题(递增子序列、数对链、定差子序列、斐波那契子序列...)

文章目录

  • 1. 前言
  • 2. 例题
    • 最长递增子序列
  • 3. 算法题
    • 3.1_摆动序列
    • 3.2_最长递增子序列的个数
    • 3.3_最长数对链
    • [3.4_ 最长定差子序列](https://leetcode.cn/problems/longest-arithmetic-subsequence-of-given-difference/description/)
    • 3.5_最长的斐波那契子序列的长度
    • 3.6_最长等差数列
    • 3.7_等差数列划分II-子序列

1. 前言

关于 动态规划的理解 与例题,点击👇

【动态规划】C++解决斐波那契模型题目(三步问题、爬楼梯、解码方法…)

并且我们之前做过有关 子数组的dp问题:C++子数组dp问题

子序列与子数组的区别主要在于:子数组是连续的,即下标连续的不中断的;而子序列可以连续可以不连续(子序列的范围更大)。

有了上面的经验,我们来解下面 子序列 问题

2. 例题

通过下面一道例题理解子序列问题,以及如何解子序列dp问题:

最长递增子序列

在这里插入图片描述

思路

  • 题意分析
    1. 题目要求找到 最长的递增子序列的长度

根据题目要求,我们设置状态表示:

在这里插入图片描述
根据上面的思路,我们可以用两个for循环编写呆猫:

代码

class Solution {
public:int lengthOfLIS(vector<int>& nums) {int n = nums.size();// 创建dp数组 + 初始化vector<int> dp(n, 1); // dp[i]: 以i为结尾,严格递增子序列的最长长度int ret = 1; // 结果:for(int i = 1; i < n; ++i){for(int j = 0; j <= i-1; ++j){if(nums[j] < nums[i])dp[i] = max(dp[j]+1, dp[i]);}ret = max(ret, dp[i]);}return ret;}
};

需要注意的是:本题的最优解法并不是利用动态规划,但通过本道例题可以很好的对子序列问题进行理解:

(顺带贴上更优解法:贪心 + 二分)

class Solution {
public:int lengthOfLIS(vector<int>& nums) {// 贪心vector<int> ret;ret.push_back(nums[0]);for(int i = 0; i < nums.size(); ++i){if(nums[i] > ret.back()) {ret.push_back(nums[i]);} else { // 二分查找int left = 0, right = ret.size() - 1;while(left < right){int mid = (left + right) >> 1;if(ret[mid] < nums[i])left = mid + 1;elseright = mid;}ret[left] = nums[i]; // 插入}}return ret.size();}
};

3. 算法题

通过《最长递增子序列》一题给的经验,我们来解下面的算法题:

3.1_摆动序列

在这里插入图片描述

思路

  • 题意分析
    1. 在子数组问题中,我们曾做过一道题《最长湍流子数组》,和本题类似,我们根据它的经验,可以创建两个dp表:

在这里插入图片描述
在这里插入图片描述

代码

class Solution {
public:int wiggleMaxLength(vector<int>& nums) {int n = nums.size();// dp数组的创建 + 元素初始化vector<int> f(n, 1); // 以i为结尾,最后呈现“上升”状态的 子序列的最长长度vector<int> g(n, 1); // 以i为结尾,最后呈现“下降”状态的 子序列的最长长度int ret = 1; // 最终结果for(int i = 1; i < n; ++i){for(int j = 0; j <= i - 1; ++j){if(nums[j] < nums[i]) f[i] = max(g[j]+1, f[i]); // 可以将第 i 个元素加入到以第 j 个元素结尾的递增子序列中if(nums[j] > nums[i]) g[i] = max(f[j]+1, g[i]);}ret = max(max(f[i], g[i]), ret);}return ret;}
};

3.2_最长递增子序列的个数

在这里插入图片描述

思路

  • 题意分析
    1. 之前的例题,求的是最长递增子序列的长度,这里要的是 最长递增子序列的 个数
    2. 即我们不仅需要考虑长度,也需要考虑个数,即两个状态,可以设置两个dp表
    3. 一个小demo: 如何找到数组中最大值的出现次数?
      • 遍历数组会有三种情况:
        • x < maxVal: 无视
        • x == maxVal: count++
        • x > maxVal: 更新maxVal,count = 0

根据这个思路,我们进行分析:

在这里插入图片描述

在这里插入图片描述

代码

class Solution {
public:int findNumberOfLIS(vector<int>& nums) {int n = nums.size();// 创建dp数组 + 初始化元素vector<int> len(n, 1), count(n, 1); // 分别为:以i为结尾,1.递增子序列的最长长度 与 2.最长递增子序列的个数int retLen = 1, retCount = 1;for(int i = 1; i < n; ++i){for(int j = 0; j <= i-1; ++j){if(nums[j] < nums[i]){if(len[j] + 1 == len[i]) count[i] += count[j]; // 第j位恰好与i组成最长递增子序列// else if(len[j] + 1 < len[i]) continue; // 无视此次,可以注释掉else if(len[j] + 1 > len[i]) len[i] = len[j] + 1, count[i] = count[j]; // j的递增序列更长}}// 更新结果if(retLen == len[i]) retCount += count[i];else if(retLen < len[i]){retCount = count[i];retLen = len[i];}// else 无视该情况}return retCount;}
};

3.3_最长数对链

在这里插入图片描述

思路

  • 题意分析
    1. 题目要求找到 最长的数对链,数对链即对于[a, b], [c, d]仅当b < c时,才成立[a, b] -> [c, d]
    2. 根据这个规律,我们在找子序列时,首先要保证不能存在一种情况:
      • 令存在三个数对x, y, z
      • 当我们遍历到y时,即使不一定有y->z,但一定不能有z->y
    3. 根据数对链的性质,我们只需要 对数组根据首位元素进行排序 即可。
    4. 由此可以设置状态表示:

在这里插入图片描述

代码

class Solution {
public:int findLongestChain(vector<vector<int>>& pairs) {// 预处理:排序数组sort(pairs.begin(), pairs.end());int n = pairs.size();// 创建dp数组 + 初始化元素vector<int> dp(n, 1); // dp[i]: 以i为结尾,数对链的最长长度int ret = 1; // 结果for(int i = 1; i < n; ++i){for(int j = 0; j <= i-1; ++j){if(pairs[j][1] < pairs[i][0]){dp[i] = max(dp[j] + 1, dp[i]);}}ret = max(ret, dp[i]);}return ret;}
};

3.4_ 最长定差子序列

在这里插入图片描述

思路

  • 题意分析
    1. 题目要求找 最长的定差子序列的长度 ,定差子序列即等差数列,给定了公差difference。
    2. 根据题目要求设置装填表示:

在这里插入图片描述
在这里插入图片描述

代码

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, n = arr.size();for(int i = 1; i < n; ++i){hash[arr[i]] = hash[arr[i] - difference] + 1; // dp[i] = dp[j] + 1,可以保证是最后一个bret = max(ret, hash[arr[i]]);}return ret;}
};

3.5_最长的斐波那契子序列的长度

在这里插入图片描述

思路

  • 题意分析
    1. 题目要求找 最长的斐波那契子序列的长度
    2. 我们首先试着将状态表示设置为:
      • dp[i]:以i为结尾的所有子序列中,最长斐波那契子序列的长度
      • 当我们尝试写状态表达式时,没有办法正确找到(斐波那契子序列的判断至少需要两个元素,通过差值我们可以判断另一位是什么)
    3. 所以我们更改状态表示:

在这里插入图片描述
在这里插入图片描述

代码

class Solution {
public:int lenLongestFibSubseq(vector<int>& arr) {int n = arr.size();// 创建dp数组 + 元素初始化vector<vector<int>> dp(n, vector<int>(n, 2));// 哈希表:值映射下标unordered_map<int, int> hash;for(int i = 0; i < arr.size(); ++i)hash[arr[i]] = i; int ret = 2; // 返回值for(int j = 2; j < n; ++j){for(int i = 1; i < j; ++i){// 填表int a = arr[j] - arr[i];if(a < arr[i] && hash.count(a))dp[i][j] = dp[hash[a]][i] + 1; // ret = max(ret, dp[i][j]);}}return ret == 2 ? 0 : ret;}
};

3.6_最长等差数列

在这里插入图片描述

思路

  • 题意分析
    1. 本题与前面的斐波那契子序列有相似之处,下面进行状态表示的设置:

在这里插入图片描述
在这里插入图片描述

代码

class Solution {
public:int longestArithSeqLength(vector<int>& nums) {int n = nums.size();unordered_map<int, int> hash; // <元素 下标>hash[nums[0]] = 0;vector<vector<int>> dp(n, vector<int>(n, 2));// dp表的创建+初始化int ret = 2;for(int i = 1; i < n; ++i) // 固定倒数第二个数{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; // dp[k][i] + 1ret = max(ret, dp[i][j]);}hash[nums[i]] = i; // 保存最近的元素下标 }return ret;}
};

3.7_等差数列划分II-子序列

在这里插入图片描述

思路

  • 题意分析
    1. 前面的题要求最长等差数列的长度,而本题要求个数
    2. 对于本题我们可以采用上题思路的①优化以及①填表顺序

代码

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);vector<vector<long long>> dp(n, vector<long long>(n, 0));// dp表的创建+初始化long long sum = 0;for(int j = 2; j < n; ++j) // 固定倒数第二个数{for(int i = 1; i < j; ++i) // 固定最后一个数{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; // += dp[k][i] + 1sum += dp[i][j];}}return sum;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jvm知识点总结(一)

JVM的跨平台 java程序一次编写到处运行。java文件编译生成字节码&#xff0c;jvm将字节码翻译成不同平台的机器码。 JVM的语言无关性 JVM只是识别字节码&#xff0c;和语言是解耦的&#xff0c;很多语言只要编译成字节码&#xff0c;符合规范&#xff0c;就能在JVM里运行&am…

小程序AI智能名片商城系统直连:打造用户与企业无缝对接的新时代!

在高度不确定性的商业环境中&#xff0c;企业如何快速响应市场变化&#xff0c;实现与用户的零距离接触&#xff1f;答案就是——小程序AI智能名片商城系统直连&#xff01;这一创新工具不仅为企业打开了与用户直接连接的大门&#xff0c;更为企业提供了持续收集用户反馈、快速…

基于Python+Selenium+Pytest的Dockerfile如何写

使用 Dockerfile 部署 Python 应用程序与 Selenium 测试 在本文中&#xff0c;我们将介绍如何使用 Dockerfile 部署一个 Python 应用程序&#xff0c;同时利用 Selenium 进行自动化测试。我们将使用官方的 Python 运行时作为父镜像&#xff0c;并在其中安装所需的依赖项和工具…

3 命名实体识别调优化

能走到这里说明你对模型微调有了一个基本的认识。那么开始一段命名实体的任务过程&#xff0c;下面使用huggingface官网的数据。 1 准备模型 下面的模型自己选择一个吧&#xff0c;我的内存太第一个模型跑不了。 https://huggingface.co/ckiplab/bert-base-chinese-ner/tree…

stack、queue(priority_queue)的模拟实现和deque的简单介绍

stack和queue(priority_queue) 1. 容器适配器 适配器(Adapter)&#xff1a;一种用来修饰容器(Containers)或仿函数(Functors)或迭代器(Iterator)接口的东西。 适配器是一种设计模式&#xff0c;该模式将一个类的接口转换成客户希望的另外一个接口。 现实中拿插座来说&#xf…

react引入iconfont的svg图标

react引入iconfont的svg图标 本文目录 react引入iconfont的svg图标普通图标通过link引入css组件内引入css使用 svg图标通过script引入js组件内引入js使用 通过封装组件自定义封装组件中调用 通过antd封装使用 普通图标 通过link引入css <link rel"stylesheet" h…

无门槛做数据可视化,真的假的?

做数据可视化确实没有太高的门槛&#xff0c;不管是企业还是个人都能找到适合自己的BI数据可视化工具&#xff0c;通过点击、拖拉的操作实现自主分析、智能分析、数据可视化&#xff0c;还可实现大屏可视化、移动端数据可视化分析&#xff0c;实现实施监控数据、日常运营决策、…

验证 python解释器是否安装成功

一. 简介 前一篇文章学习了下载并安装 python解释器&#xff0c;文章如下&#xff1a; windows系统下python解释器安装-CSDN博客 本文验证 python解释器是否安装成功。 二. 验证 python解释器是否安装成功 1. 首先&#xff0c;打开 Windows系统的 "cmd" 界面。…

2024商业地产五一劳动节健康大会朋克养生市集活动策划方案

2024商业地产五一劳动节健康大会朋克养生市集&#xff08;带薪健康 快乐打工主题&#xff09;活动策划方案 活动策划信息&#xff1a; 方案页码&#xff1a;53页 文件格式&#xff1a;PPT 方案简介&#xff1a; 打工不养生 赚钱养医生 期待已久的五一假期&#xff0c; …

wandb注册 wandb: ERROR api_key

wandb: ERROR api_key not configured (no-tty). call wandb.login(key[your_api_key]) Traceback (most recent call last): 背景 使用yolov8训练时 在pycharm中出现wandb账号未注册错误 Transferred 355/355 items from pretrained weights TensorBoard: Start with tensor…

Day13-Java进阶-IO字节流字符流及其练习题

1. IO流介绍 2. IO 流体系结构 字节流读取纯文本文件会出现乱码问题 2.1 FileOutputStream 字节输出流 package com.itheima.stream.output;import java.io.FileOutputStream; import java.io.IOException;public class FileOutputStreamDemo3 {/*IO流的异常处理方式: jdk7版本…

58岁第一代「晶女郎」激罕现身

90年代性感女神关秀媚在2006年拍完内地剧集《暴雨梨花》后更全面息影&#xff0c;而且更甚少现身于人前。日前曾志伟庆祝71岁生日&#xff0c;举行盛大慈善素宴广邀圈中好友&#xff0c;为寺庙重建工程筹募经费。女神关秀媚便罕有接受访问透露近况。 当天关秀媚将头发盘起&…