当前位置: 首页 > news >正文

线性动态规划

具有「线性」阶段划分的动态规划方法统称为线性动态规划(简称为「线性 DP」),如下图所示。

一、概念

如果状态包含多个维度,但是每个维度上都是线性划分的阶段,也属于线性 DP。比如背包问题、区间 DP、数位 DP 等都属于线性 DP。

线性 DP 问题的划分方法有多种方式。

  • 如果按照「状态的维度数」进行分类,我们可以将线性 DP 问题分为:一维线性 DP 问题、二维线性 DP 问题,以及多维线性 DP 问题。
  • 如果按照「问题的输入格式」进行分类,我们可以将线性 DP 问题分为:单串线性 DP 问题、双串线性 DP 问题、矩阵线性 DP 问题,以及无串线性 DP 问题。

二、单串线性 DP 问题

1. 问题

单串线性 DP 问题:问题的输入为单个数组或单个字符串的线性 DP 问题。状态一般可定义为 dp[i],表示为:

  1. 「以数组中第 i个位置元素 nums[i]] 为结尾的子数组(nums[0]...nums[i])」的相关解。
  2. 「以数组中第 i−1个位置元素 nums[i−1]为结尾的子数组(nums[0]...nums[i−1])」的相关解。
  3. 「以数组中前 i个元素为子数组(nums[0]...nums[i−1])」的相关解

这 3 种状态的定义区别在于相差一个元素 nums[i]。

  1. 第 1种状态:子数组的长度为 i+1,子数组长度不可为空;
  2. 第 2 种状态、第 3 种状态:这两种状态描述是相同的。子数组的长度为 i,子数组长度可为空。在 i=0时,方便用于表示空数组(以数组中前 0 个元素为子数组)

三、最长递增子序列

单串线性 DP 问题中最经典的问题就是「最长递增子序列(Longest Increasing Subsequence,简称 LIS)」。

1. 实战练习

给定一个整数数组 nums,找到其中最长严格递增子序列的长度

  • 子序列:由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
  • 1≤nums.length≤2500。
  • −10^4≤nums[i]≤10^4。

2. 代码

class Solution {// 定义长度最长递增子序列的方法lengthOfLIS(nums) {// 获取数组的长度const size = nums.length;// 创建并初始化动态规划数组,初始值为1const dp = new Array(size).fill(1);// 外层循环迭代每个元素for (let i = 0; i < size; i++) {// 内层循环迭代当前元素之前的元素for (let j = 0; j < i; j++) {// 如果当前元素大于之前的元素,可以将当前元素加入递增子序列if (nums[i] > nums[j]) {// 更新以当前元素结尾的递增子序列的长度dp[i] = Math.max(dp[i], dp[j] + 1);}}}// 返回动态规划数组中的最大值,即为最长递增子序列的长度return Math.max(...dp);}
}// 示例用法
const solution = new Solution();
// 输出最长递增子序列的长度
console.log(solution.lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]));

四、最大子数组和

单串线性 DP 问题中除了子序列相关的线性 DP 问题,还有子数组相关的线性 DP 问题。

注意

  • 子序列:由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。
  • 子数组:指的是数组中的一个连续子序列。

「子序列」与「子数组」都可以看做是原数组的一部分,而且都不会改变原来数组中元素的相对顺序。其区别在于数组元素是否要求连续。

1. 实战练习

给定一个整数数组 nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和

2. 代码

class Solution {maxSubArray(nums) {// 获取数组的长度const size = nums.length;// 创建并初始化动态规划数组,初始值为0const dp = new Array(size).fill(0);// 初始化动态规划数组的第一个元素dp[0] = nums[0];// 从数组的第二个元素开始遍历for (let i = 1; i < size; i++) {// 如果前一个元素的动态规划值小于0,说明不利于累积当前元素,直接以当前元素为起点重新计算if (dp[i - 1] < 0) {dp[i] = nums[i];} else {// 否则,累积当前元素,更新动态规划值dp[i] = dp[i - 1] + nums[i];}}// 返回动态规划数组中的最大值,即为最大子数组和return Math.max(...dp);}
}// 示例用法
const solution = new Solution();
// 输出最大子数组和
console.log(solution.maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4]));

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

有一些单串线性 DP 问题在定义状态时需要考虑两个结束位置,只考虑一个结束位置的无法清楚描述问题。这时候我们就需要需要增加一个结束位置维度来定义状态

1. 实战练习

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

2. 代码

class Solution {lenLongestFibSubseq(arr) {const size = arr.length;let ans = 0;// 枚举所有可能的前两个数for (let i = 0; i < size; i++) {for (let j = i + 1; j < size; j++) {let tempAns = 0;let tempI = i;let tempJ = j;let k = j + 1;// 在数组中搜索斐波那契子序列while (k < size) {// 如果当前三个数满足斐波那契关系if (arr[tempI] + arr[tempJ] === arr[k]) {tempAns += 1;tempI = tempJ;tempJ = k;}k += 1;}// 更新最大斐波那契子序列长度if (tempAns > ans) {ans = tempAns;}}}// 如果找到了斐波那契子序列,返回长度加上初始的两个数if (ans > 0) {return ans + 2;} else {return ans;}}
}// 示例用法
const solution = new Solution();
// 输出最长斐波那契子序列的长度
console.log(solution.lenLongestFibSubseq([1, 2, 3, 4, 5, 6, 7, 8])); 

相关文章:

  • Java面试八股--07-项目篇
  • AI“实体化”革命:具身智能如何重构体育、工业与未来生活
  • 数据库包括哪些?关系型数据库是什么意思?
  • “刹车思维”:慢,是为了更快
  • 什么是链游,链游系统开发价格以及方案
  • ESOP股权管理平台完整解决方案
  • 北京大学肖臻老师《区块链技术与应用》公开课:07-BTC-挖矿难度
  • superior哥AI系列第6期:Transformer注意力机制:AI界的“注意力革命“
  • 三、Sqoop 全量导入核心命令
  • 【摘录】显示屏购买要注意的参数
  • nginx去掉暴漏外边的版本号
  • 普中STM32F103ZET6开发攻略(四)
  • 06 APP 自动化- H5 元素定位
  • 冷链物流智能监控终端技术方案——EFISH-SCB-RK3588国产化替代实践
  • Mybatis--创建mapper接口
  • 知识库检索方法详解:稀疏 vs 稠密;《第一篇》
  • Temporal Fusion Transformer(TFT)扩散模型时间序列预测模型
  • 【Pandas】pandas DataFrame rename
  • Java 中执行命令并使用指定配置文件的最佳实践
  • 在 Dify 项目中的 Celery:异步任务的实现与集成
  • 杭州做网站公司哪家好/百度自然搜索排名优化
  • 手机网站设计案例/苏州网站制作开发公司
  • 培训机构网站制作/seo营销优化软件
  • 推荐几个响应式网站做参考/外链工厂 外链
  • 做网站怎么样才能排在首页/网络营销属于什么专业类型
  • 政府网站登录界面模板/搜索引擎成功案例分析