算法沉淀——动态规划之子序列问题(下)(leetcode真题剖析)

在这里插入图片描述

算法沉淀——动态规划之子序列问题

  • 01.最长定差子序列
  • 02.最长的斐波那契子序列的长度
  • 03.最长等差数列
  • 04.等差数列划分 II - 子序列

01.最长定差子序列

题目链接:https://leetcode.cn/problems/longest-arithmetic-subsequence-of-given-difference/

给你一个整数数组 arr 和一个整数 difference,请你找出并返回 arr 中最长等差子序列的长度,该子序列中相邻元素之间的差等于 difference

子序列 是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从 arr 派生出来的序列。

示例 1:

输入:arr = [1,2,3,4], difference = 1
输出:4
解释:最长的等差子序列是 [1,2,3,4]。

示例 2:

输入:arr = [1,3,5,7], difference = 1
输出:1
解释:最长的等差子序列是任意单个元素。

示例 3:

输入:arr = [1,5,7,8,5,3,4,2,1], difference = -2
输出:4
解释:最长的等差子序列是 [7,5,3,1]。 

提示:

  • 1 <= arr.length <= 105
  • -104 <= arr[i], difference <= 104

思路

  1. 状态表达: 定义动态规划数组 dp,其中 dp[i] 表示以第 i 个位置的元素为结尾的所有子序列中,最长的等差子序列的长度。
  2. 状态转移方程: 对于 dp[i],上一个定差子序列的取值定为 arr[i] - difference。只要找到以上一个数为结尾的定差子序列长度的 dp[arr[i] - difference],然后加上 1,就是以 i 为结尾的定差子序列的长度。这里可以使用哈希表进行优化,将元素和 dp[j] 绑定,放入哈希表中。
  3. 初始化: 刚开始的时候,需要把第一个元素放进哈希表中,即 hash[arr[0]] = 1
  4. 填表顺序: 根据状态转移方程,填表顺序是从左往右。
  5. 返回值: 根据状态表达,返回整个 dp 数组中的最大值。

代码

class Solution {
public:int longestSubsequence(vector<int>& arr, int difference) {unordered_map<int,int> hash;hash[arr[0]]=1;int ret=1;for(int i=1;i<arr.size();i++){hash[arr[i]]=hash[arr[i]-difference]+1;ret=max(ret,hash[arr[i]]);}return ret;}
};

02.最长的斐波那契子序列的长度

题目链接:https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence/

如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的:

  • n >= 3
  • 对于所有 i + 2 <= n,都有 X_i + X_{i+1} = X_{i+2}

给定一个严格递增的正整数数组形成序列 arr ,找到 arr 中最长的斐波那契式的子序列的长度。如果一个不存在,返回 0 。

(回想一下,子序列是从原序列 arr 中派生出来的,它从 arr 中删掉任意数量的元素(也可以不删),而不改变其余元素的顺序。例如, [3, 5, 8][3, 4, 5, 6, 7, 8] 的一个子序列)

示例 1:

输入: arr = [1,2,3,4,5,6,7,8]
输出: 5
解释: 最长的斐波那契式子序列为 [1,2,3,5,8] 。

示例 2:

输入: arr = [1,3,7,11,12,14,18]
输出: 3
解释: 最长的斐波那契式子序列有 [1,11,12]、[3,11,14] 以及 [7,11,18] 。

提示:

  • 3 <= arr.length <= 1000
  • 1 <= arr[i] < arr[i + 1] <= 10^9

思路

  1. 状态表达: 定义动态规划数组 dp,其中 dp[j][i] 表示以第 j 位置以及第 i 位置的元素为结尾的所有的子序列中,最长的斐波那契子序列的长度。
  2. 状态转移方程:nums[j] = bnums[i] = c,那么这个序列的前一个元素就是 a = c - b。根据 a 的情况讨论:
    • 如果 a 存在,下标为 k,并且 a < b,那么 dp[j][i] = dp[k][j] + 1
    • 如果 a 存在,但是 b < a < c,那么 dp[j][i] = 2
    • 如果 a 不存在,那么 dp[j][i] = 2
  3. 优化点: 在状态转移方程中,需要确定 a 元素的下标,可以在填表之前,将所有的「元素 + 下标」绑定在一起,放到哈希表中。
  4. 初始化: 将表里面的值都初始化为 2
  5. 填表顺序:
    • 先固定最后一个数;
    • 然后枚举倒数第二个数。
  6. 返回值: 返回 dp 表中的最大值 ret。但是 ret 可能小于 3,小于 3 说明不存在,需要判断一下。

代码

class Solution {
public:int lenLongestFibSubseq(vector<int>& arr) {int n=arr.size();unordered_map<int,int> hash;for(int i=0;i<n;i++) hash[arr[i]]=i;vector<vector<int>> dp(n,vector<int>(n,2));int ret=2;for(int i=2;i<n;++i){for(int j=1;j<i;j++){int x=arr[i]-arr[j];if(x<arr[j]&&hash.count(x))dp[j][i] = dp[hash[x]][j]+1;ret = max(ret,dp[j][i]);}}return ret<3?0:ret;}
};

03.最长等差数列

题目链接:https://leetcode.cn/problems/longest-arithmetic-subsequence/

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

回想一下,nums 的子序列是一个列表 nums[i1], nums[i2], ..., nums[ik] ,且 0 <= i1 < i2 < ... < ik <= nums.length - 1。并且如果 seq[i+1] - seq[i]( 0 <= i < seq.length - 1) 的值都相同,那么序列 seq 是等差的。

示例 1:

输入:nums = [3,6,9,12]
输出:4
解释: 
整个数组是公差为 3 的等差数列。

示例 2:

输入:nums = [9,4,7,2,10]
输出:3
解释:
最长的等差子序列是 [4,7,10]。

示例 3:

输入:nums = [20,1,15,3,10,5,8]
输出:4
解释:
最长的等差子序列是 [20,15,10,5]。 

提示:

  • 2 <= nums.length <= 1000
  • 0 <= nums[i] <= 500

思路

  1. 状态表达: 定义动态规划数组 dp,其中 dp[i][j] 表示以第 i 位置以及第 j 位置的元素为结尾的所有的子序列中,最长的等差序列的长度。
  2. 状态转移方程:nums[i] = bnums[j] = c,那么这个序列的前一个元素就是 a = 2 * b - c。根据 a 的情况讨论:
    • 如果 a 存在,下标为 k,并且 a < b,那么我们需要以 k 位置以及 i 位置元素为结尾的最长等差序列的长度,然后再加上 j 位置的元素即可。于是 dp[i][j] = dp[k][i] + 1。这里因为会有许多个 k,我们仅需离 i 最近的 k 即可。因此任何最长的都可以以 k 为结尾;
    • 如果 a 存在,但是 b < a < c,那么 dp[i][j] = 2
    • 如果 a 不存在,那么 dp[i][j] = 2
  3. 优化点: 在状态转移方程中,需要确定 a 元素的下标。可以一边动态规划,一边保存最近的元素的下标,不用保存下标数组。遍历的时候,先固定倒数第二个数,再遍历倒数第一个数。这样可以在 i 使用完时候,将 nums[i] 扔到哈希表中。
  4. 初始化: 将表里面的值都初始化为 2
  5. 填表顺序:
    • 先固定倒数第二个数;
    • 然后枚举倒数第一个数。
  6. 返回值: 返回 dp 表中的最大值。

代码

class Solution {
public:int longestArithSeqLength(vector<int>& nums) {unordered_map<int,int> hash;hash[nums[0]]=0;int n=nums.size();vector<vector<int>> dp(n,vector<int>(n,2));int ret=2;for(int i=1;i<n;i++){for(int j=i+1;j<n;j++){int x=2*nums[i]-nums[j];if(hash.count(x)) dp[i][j] = dp[hash[x]][i] + 1;ret=max(ret,dp[i][j]);}hash[nums[i]]=i;}return ret;}
};

04.等差数列划分 II - 子序列

题目链接:https://leetcode.cn/problems/arithmetic-slices-ii-subsequence/

给你一个整数数组 nums ,返回 nums 中所有 等差子序列 的数目。

如果一个序列中 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该序列为等差序列。

  • 例如,[1, 3, 5, 7, 9][7, 7, 7, 7][3, -1, -5, -9] 都是等差序列。
  • 再例如,[1, 1, 2, 5, 7] 不是等差序列。

数组中的子序列是从数组中删除一些元素(也可能不删除)得到的一个序列。

  • 例如,[2,5,10][1,2,1,***2***,4,1,***5\***,***10***] 的一个子序列。

题目数据保证答案是一个 32-bit 整数

示例 1:

输入:nums = [2,4,6,8,10]
输出:7
解释:所有的等差子序列为:
[2,4,6]
[4,6,8]
[6,8,10]
[2,4,6,8]
[4,6,8,10]
[2,4,6,8,10]
[2,6,10]

示例 2:

输入:nums = [7,7,7,7,7]
输出:16
解释:数组中的任意子序列都是等差子序列。

提示:

  • 1 <= nums.length <= 1000
  • -231 <= nums[i] <= 231 - 1

思路

  1. 状态表达: 定义动态规划数组 dp,其中 dp[i][j] 表示以第 i 位置以及第 j 位置的元素为结尾的所有的子序列中,等差子序列的个数。
  2. 状态转移方程:nums[i] = bnums[j] = c,那么这个序列的前一个元素就是 a = 2 * b - c。根据 a 的情况讨论:
    • 如果 a 存在,下标为 k,并且 a < b,那么以 k 元素以及 i 元素结尾的等差序列的个数为 dp[k][i],在这些子序列的后面加上 j 位置的元素依旧是等差序列。但是这里会多出来一个以 k, i, j 位置的元素组成的新的等差序列,因此 dp[i][j] += dp[k][i] + 1
    • 因为 a 可能有很多个,需要全部累加起来。
  3. 优化点: 在状态转移方程中,需要确定 a 元素的下标。因此在 dp 之前,将所有元素和下标数组绑定在一起,放到哈希表中。这里保存下标数组是因为需要统计个数。
  4. 初始化: 刚开始是没有等差数列的,因此初始化 dp 表为 0
  5. 填表顺序:
    • 先固定倒数第一个数;
    • 然后枚举倒数第二个数。
  6. 返回值: 统计所有的等差子序列,返回 dp 表中所有元素的和。

代码

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<int>> dp(n,vector<int>(n));int sum=0;for(int j=2;j<n;j++){for(int i=1;i<j;i++){long long x=(long long)nums[i]*2-nums[j];if(hash.count(x)) for(int& k:hash[x])if(k<i) dp[i][j]+=dp[k][i]+1;sum+=dp[i][j];}}return sum;}
};

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

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

相关文章

编码技巧——Springboot工程加密yml配置/Maven引入本地二方包

1. 背景 基于Springboot的工程项目&#xff0c;通常很多信息都是在application.yml中直接明文配置的&#xff0c;比如数据库链接信息&#xff0c;redis链接信息等&#xff1b; 为了安全考虑&#xff0c;公司打算将yml配置文件中的数据库连接信息的账号&#xff0c;密码进行加…

mini-spring|关于Bean对象作用域以及FactoryBean的实现和使用

需求 FactoryBean 直接配置FactoryBean 获取FactoryBean中的Bean对象 FactoryBean的getObject方法通过反射获取Bean对象 由此省去对实体Dao类的定义 解决方法 对外提供一个可以二次从 FactoryBean 的 getObject 方法中获取对象的功能即可 整体架构 整个的实现过程包括了两部…

Redis持久化的两种方式RDB和AOF详解

小伙伴们好&#xff0c;欢迎关注&#xff0c;一起学习&#xff0c;无限进步 以下内容为学习 Redis 过程中的笔记 文章目录 Redis持久化RDB&#xff08; Redis DataBase &#xff09;触发机制&#xff1a;如何恢复rbd文件&#xff1a;优点&#xff1a;缺点&#xff1a; AOF &…

【文献管理】zotero插件4——获取知网pdf、中文文献识别与目录生成

文章目录 zotero获取知网PDFzotero——中文文献识别&#xff08;茉莉花插件&#xff09;学位论文目录生成 zotero获取知网PDF zotero——中文文献识别&#xff08;茉莉花插件&#xff09; 为下载的学位论文添加目录中文文献识别&#xff1a;jasminum 下载pdflabs下载茉莉花插…

C++——类的6个默认成员函数

目录 类中的6个默认成员函数 构造函数 构造函数的特点 初始化列表 隐式类型转换 析构函数 拷贝构造函数 赋值重载 运算符重载 赋值重载 取地址重载 类中的6个默认成员函数 类中的6个默认成员函数根据不同的作用可以分为&#xff1a; 初始化和使用后清理&#xff1a;…

YOLOv8改进,添加GSConv+Slim Neck,有效提升目标检测效果,代码改进(超详细)

目录 摘要 主要想法 GSConv GSConv代码实现 slim-neck slim-neck代码实现 yaml文件 完整代码分享 总结 摘要 目标检测是计算机视觉中重要的下游任务。对于车载边缘计算平台来说&#xff0c;巨大的模型很难达到实时检测的要求。而且&#xff0c;由大量深度可分离卷积层构…

Java Web(十一)--JSON Ajax

JSON JSon在线文档&#xff1a; JSON 简介 JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。轻量级指的是跟xml做比较。数据交换指的是客户端和服务器之间业务数据的传递格式。 它基于 ECMAScript (W3C制定的JS规范)的一个子集&#xff0c;采…

Stable Cascade发布:比SDXL更快、更强的图像生成模型

前言 StabilityAI在春节期间发布了新的一代文生图模型Stable Cascade&#xff0c;Stable Cascade是基于Wuerstchen架构包含三阶段的文生图扩散模型&#xff0c;为质量、灵活性、微调和效率设定了新的标准&#xff0c;着重于进一步消除硬件障碍。相比Stable Diffusion XL&#…

《Decoupling Representation and Classifier for Long-Tailed Recognition》阅读笔记

论文标题 《Decoupling Representation and Classifier for Long-Tailed Recognition》 用于长尾识别的解耦表示和分类器 作者 Bingyi Kang、Saining Xie、Marcus Rohrbach、Zhicheng Yan、 Albert Gordo、Jiashi Feng 和 Yannis Kalantidis 来自 Facebook AI 和 新加坡国…

Linux笔记--文件权限

一、相关概念 Linux最优秀的地方之一就在于多人多任务环境。为了让各个使用者有较为保密的文件数据&#xff0c;文件的权限管理尤为重要。 ●文件的可存取身份: owner:文件拥有者 group:文件所属用户组 others:其他人 ●文件权限: r: read&#xff0c;读 文件:是否能查看文件内…

Carla自动驾驶仿真八:两种查找CARLA地图坐标点的方法

文章目录 前言一、通过Spectator获取坐标二、通过道路ID获取坐标总结 前言 CARLA没有直接的方法给使用者查找地图坐标点来生成车辆&#xff0c;这里推荐两种实用的方法在特定的地方生成车辆。 一、通过Spectator获取坐标 1、Spectator&#xff08;观察者&#xff09;&#xf…

2W字-35页PDF谈谈自己对QT某些知识点的理解

2W字-35页PDF谈谈自己对QT某些知识点的理解 前言与总结总体知识点的概况一些笔记的概况笔记阅读清单 前言与总结 最近&#xff0c;也在对自己以前做的项目做一个知识点的梳理&#xff0c;发现可能自己以前更多的是用某个控件&#xff0c;以及看官方手册&#xff0c;但是没有更…

深入探究【观察者模式】:简单音乐会售票系统案例分析

文章目录 1.观察者模式概述基本概念&#xff1a;工作原理&#xff1a; 2.案例-音乐会抢票2.1.具体实现2.1.1.被观察者接口2.1.2.被观察者实现类2.1.3.定义观察者接口2.1.3.定义观察者实现类2.1.4.测试观察者 3.总结3.1.优点和局限性3.2.思考 1.观察者模式概述 观察者模式是一种…

如何远程访问内网数据库?

远程访问内网数据库是在安全可靠的前提下&#xff0c;能够实现从外部网络访问内网数据库的一种技术。在现代信息化的背景下&#xff0c;随着企业发展和分布式办公的普及&#xff0c;远程访问内网数据库成为了一项必需的技术。通过远程访问内网数据库&#xff0c;企业可以在不同…

2.3~2.7碎片

P是位置&#xff0c;v是速度

弹窗内容由后端返回,如何让点击按钮的事件交由前端控制?

一、场景 背景&#xff1a;因为系统里经常有新活动或者公告需要通知所有用户&#xff0c;希望前端维护的这个弹窗里的内容可以由后端接口返回。这样就不需要每次上新活动的时候&#xff0c;前端项目都发版了。因此&#xff0c;前端维护了这个弹窗和它的关闭事件&#xff0c;至…

qt5.15 升级 qt 6.5 部分问题 解决修复

报错 QT5_USE_MODULES 升级 QT6_ADD_RESOURCES qt_add_resources Compiles binary resources into source code. CMake Commands in Qt6 Core | Qt Core 6.6.2

用Flutter开发App:助力您的移动业务腾飞

一、Flutter简介 Flutter是Google推出的用于构建多平台应用程序的开源UI框架。它使用Dart语言编写&#xff0c;可以编译为原生机器代码&#xff0c;从而提供卓越的性能和流畅的用户体验。 二、Flutter的优势 一套代码&#xff0c;多平台部署&#xff1a;Flutter可以使用一套代…

《互联网的世界》第二讲-最短路径优先

昨天讲 dns 时讲过&#xff0c;“你问一个当地人最近的厕所在哪&#xff0c;路人给你一个地址…”&#xff0c;可是只有地址还不够&#xff0c;如何到达那里呢&#xff1f;这是本节的内容。 自然的方式是&#xff0c;一边走一边问&#xff0c;根据路人的指示继续一边走一边问…

德人合科技 | 天锐绿盾终端安全管理系统

德人合科技提到的“天锐绿盾终端安全管理系统”是一款专业的信息安全防泄密软件。这款软件基于核心驱动层&#xff0c;为企业提供信息化防泄密一体化方案。 www.drhchina.com 其主要特点包括&#xff1a; 数据防泄密管理&#xff1a;天锐绿盾终端安全管理系统能够确保数据在创…