当前位置: 首页 > 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])); 

http://www.xdnf.cn/news/793225.html

相关文章:

  • 张雪峰为9岁女儿申请40个左右商标!
  • 超声波粒度仪市场报告:行业现状、竞争格局与未来趋势分析
  • 原子操作与非原子操作
  • RTOS,其高级使用
  • TypeScript中class的两种继承方式extends和implements的对比
  • HTML5新特性
  • DAY 20 奇异值SVD分解
  • ant-design-vue select 下拉框不好用解决
  • Nginx 的配置文件
  • GCC内存占用统计使用指南
  • 【Android】双指旋转手势
  • AI 驱动工业:应用场景、挑战与未来趋势
  • SP网络结构:现代密码学的核心设计
  • SAP是什么?SAP概述
  • 免费论文查重与AI检测工具推荐
  • NVIDIA NVLink Fusion 是 PCIe Gen5 的 14 倍
  • pcie 日常问答-20250528
  • 累乘法求数列的通项公式
  • 手撕HashMap!(JDK7版本)
  • Unreal Niagara制作炫酷VJ粒子
  • 深入解析域名解析:原理、流程与应用实践
  • Spring 中创建 Bean 有几种方式?
  • Ajax技术深度解析:从原理到现代Web开发实践
  • 学习日记-day21-6.3
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(31):そう
  • 碰一碰发视频-源码系统开发技术分享
  • javascript 实战案例 二级联动下拉选框
  • 八.MySQL复合查询
  • 书籍在其他数都出现k次的数组中找到只出现一次的数(7)0603
  • 实战商品订单秒杀设计实现