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

【动态规划】深入动态规划 非连续子序列问题

文章目录

  • 前言
  • 例题
    • 一、最长递增子序列
    • 二、摆动序列
    • 三、最长递增子序列的个数
    • 四、最长数对链
    • 五、最长定差子序列
    • 六、最长的斐波那契子序列的长度
    • 七、最长等差数列
    • 八、等差数列划分II - 子序列
  • 结语


在这里插入图片描述

前言

什么是动态规划中的非连续子序列问题?

动态规划中的非连续子序列问题是指在一个给定的序列中,寻找满足特定条件的非连续子序列,以达到某种最优目标。
例如,在最长递增子序列(LIS)问题中,给定一个整数序列,需要找出一个子序列,使得该子序列中的元素是递增的,且子序列的长度最长。这个子序列中的元素在原序列中不一定是连续的。
解决这类问题通常使用动态规划的方法。以最长递增子序列问题为例,定义状态dp[i]表示以第i个元素结尾的最长递增子序列的长度。初始时,每个dp[i]都设为 1,因为单个元素本身就是一个长度为 1 的递增子序列。然后,对于每个i,遍历0到i - 1的元素j,如果nums[j] < nums[i],说明可以将nums[i]添加到以nums[j]结尾的递增子序列后面,更新dp[i] = max(dp[i], dp[j] + 1)。最后,在所有的dp[i]中找出最大值,就是整个序列的最长递增子序列的长度。通过这种方式,利用已解决的子问题的解来构建更大问题的解,避免了大量的重复计算,提高了算法效率。

下面,本文将以例题的形式为大家详细展开讲述动态规划里的非连续子序列问题!

例题

一、最长递增子序列

  1. 题目链接:最长递增子序列
  2. 题目描述:

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
提示:1 <= nums.length <= 2500
-10^4 <= nums[i] <= 10^4

  1. 解法(动态规划):
    算法思路:
    状态表示: 对于线性 dp ,我们可以用「经验 + 题目要求」来定义状态表示:
    i. 以某个位置为结尾,巴拉巴拉;
    ii. 以某个位置为起点,巴拉巴拉。这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义⼀个状态表示:
    dp[i] 表示:以 i 位置元素为结尾的「所有子序列」中,最长递增子序列的长度。
    状态转移方程: 对于 dp[i] ,我们可以根据「⼦序列的构成方式」,进行分类讨论:
    i. 子序列长度为 1 :只能自己玩了,此时 dp[i] = 1 ;
    ii. 子序列长度大于 1 : nums[i] 可以跟在前面任何⼀个数后面形成子序列。 设前面的某⼀个数的下标为 j ,其中 0 <= j <= i - 1 。 只要 nums[j] < nums[i] , i 位置元素跟在 j 元素后⾯就可以形成递增序列,长度 为 dp[j] + 1 。 因此,我们仅需找到满足要求的最大的 dp[j] + 1 即可。 综上, dp[i] = max(dp[j] + 1, dp[i]) ,其中 0 <= j <= i - 1 && nums[j] < nums[i] 。
    初始化: 所有的元素「单独」都能构成⼀个递增⼦序列,因此可以将 dp 表内所有元素初始化为 1 。 由于用到前面的状态,因此我们循环的时候从第⼆个位置开始即可。
    填表顺序: 显而易见,填表顺序「从左往右」。
    返回值: 由于不知道最长递增子序列以谁结尾,因此返回 dp 表里面的「最大值」

4 代码示例:

 public int lengthOfLIS(int[] nums) {int n =nums.length;int[] dp = new int[n];int ret = 1;for(int i = 0;i<n;i++)  dp[i] = 1;for(int i = 1;i<n;i++){for(int j = 0;j<i;j++){if(nums[j] < nums[i]){dp[i] = Math.max(dp[j] + 1,dp[i]);}}ret = Math.max(dp[i],ret);}return ret;}

二、摆动序列

  1. 题目链接:摆动序列
  2. 题目描述:

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第⼀个差(如果 存在的话)可能是正数或负数。仅有⼀个元素或者含两个不等元素的序列也视作摆动序列。
• 例如, [1, 7, 4, 9, 2, 5] 是⼀个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
• 相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第⼀个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后⼀个差值为零。 子序列可以通过从原始序列中删除⼀些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。 给你⼀个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的⻓度 。
示例 1:
输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。
示例 2:
输入:nums = [1,17,5,10,13,15,10,5,16,8]
输出:7
解释:这个序列包含几个长度为 7 摆动序列。 其中⼀个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。
示例 3:
输入:nums = [1,2,3,4,5,6,7,8,9]
输出:2

  1. 解法(动态规划):
    算法思路:
    状态表示: 对于线性 dp ,我们可以用「经验 + 题目要求」来定义状态表示:
    i. 以某个位置为结尾,巴拉巴拉;
    ii. 以某个位置为起点,巴拉巴拉。 这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义⼀个状态表示:
    dp[i] 表示「以 i 位置为结尾的最长摆动序列的长度」。
    但是,问题来了,如果状态表示这样定义的话,以 i 位置为结尾的最长摆动序列的长度我们没法从之前的状态推导出来。因为我们不知道前⼀个最⻓摆动序列的结尾处是递增的,还是递减的。因 此,我们需要状态表⽰能表⽰多⼀点的信息:要能让我们知道这⼀个最⻓摆动序列的结尾是递增的 还是递减的。 解决的⽅式很简单:搞两个 dp 表就好了。
    f[i] 表示:以 i 位置元素为结尾的所有的子序列中,最后⼀个位置呈现「上升趋势」的最长摆动序列的长度;
    g[i] 表示:以 i 位置元素为结尾的所有的子序列中,最后⼀个位置呈现「下降趋势」的最长摆动序列的长度。
    状态转移⽅程: 由于⼦序列的构成⽐较特殊, i 位置为结尾的子序列,前一个位置可以是 [0, i - 1] 的任意 位置,因此设 j 为 [0, i - 1] 区间内的某⼀个位置。 对于 f[i] ,我们可以根据「子序列的构成方式」,进行分类讨论:
    i. 子序列长度为 1 :只能自己玩了,此时 f[i] = 1 ;
    ii. 子序列长度大于 1 :因为结尾要呈现上升趋势,因此需要 nums[j] < nums[i] 。在满足这个条件下, j 结尾需要呈现下降状态,最⻓的摆动序列就是 g[j] + 1 。 因此我们要找出所有满足条件下的最⼤的 g[j] + 1 。 综上, f[i] = max(g[j] + 1, f[i]) ,注意使⽤ g[j] 时需要判断。
    对于 g[i] ,我们可以根据「子序列的构成⽅式」,进⾏分类讨论:
    i. ⼦序列长度为 1 :只能自己玩了,此时 g[i] = 1 ;
    ii. ⼦序列长度大于 1 :因为结尾要呈现下降趋势,因此需要 nums[j] > nums[i] 。在满足这个条件下, j 结尾需要呈现上升状态,因此最长的摆动序列就是 f[j] + 1 。 因此我们要找出所有满⾜条件下的最⼤的 f[j] + 1 。 综上, g[i] = max(f[j] + 1, g[i]) ,注意使⽤ f[j] 时需要判断。
    初始化: 所有的元素「单独」都能构成⼀个摆动序列,因此可以将 dp 表内所有元素初始化为 1 。
    填表顺序: 毫无疑问是「从左往右」。
    返回值: 应该返回「两个 dp 表里面的最大值」,我们可以在填表的时候,顺便更新⼀个「最大值」。

  2. 代码示例:

  public int wiggleMaxLength(int[] nums) {int n = nums.length;int[] f = new int[n];int[] g = new int[n];int ret = 1;for(int i = 0;i<n;i++) g[i] = f[i] = 1;for(int i = 1;i<n;i++){for(int j = 0;j<i;j++){if(nums[j]>nums[i]) f[i] = Math.max(f[i] , g[j]+1);if(nums[j]<nums[i]) g[i] = Math.max(f[j]+1,g[i]);}ret = Math.max(ret,Math.max(f[i],g[i]));}return ret;}

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

  1. 题目链接:最长递增子序列的个数
  2. 题目描述:

给定⼀个未排序的整数数组 nums , 返回最长递增子序列的个数 。 注意 这个数列必须是严格递增的。
示例 1:
输入: [1,3,5,4,7]
输出: 2 解释:
有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:
输⼊: [2,2,2,2,2]
输出: 5 解释:最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。

  1. 解法(动态规划):
    算法思路:
    状态表示: 先尝试定义⼀个状态:以 i 为结尾的最长递增子序列的「个数」。那么问题就来了,我都不知道 以 i 为结尾的最⻓递增子序列的「长度」是多少,我怎么知道最长递增子序列的个数呢? 因此,我们解决这个问题需要两个状态,一个是「长度」,⼀个是「个数」:
    len[i] 表示:以 i 为结尾的最长递增子序列的⻓度;
    count[i] 表示:以 i 为结尾的最长递增子序列的个数。
    状态转移方程: 求个数之前,我们得先知道长度,因此先看 len[i] :
    i. 在求 i 结尾的最长递增序列的⻓度时,我们已经知道 [0, i - 1] 区间上的 len[j]信息,用 j 表示 [0, i - 1] 区间上的下标;
    ii. 我们需要的是递增序列,因此 [0, i - 1] 区间上的 nums[j] 只要能和 nums[i]
    构成上升序列,那么就可以更新 dp[i] 的值,此时最长长度为 dp[j] + 1 ;
    iii. 我们要的是 [0, i - 1] 区间上所有情况下的最⼤值。 综上所述,对于 len[i] ,我们可以得到状态转移⽅程为:len[i] = max(len[j] + 1, len[i]) ,其中 0 <= j < i ,并且 nums[j] < nums[i] 。 在知道每⼀个位置结尾的最⻓递增⼦序列的⻓度时,我们来看看能否得到 count[i] :
    i. 我们此时已经知道 len[i] 的信息,还知道 [0, i - 1] 区间上的 count[j] 信 息,用 j 表示 [0, i - 1] 区间上的下标;
    ii. 我们可以再遍历⼀遍 [0, i - 1] 区间上的所有元素,只要能够构成上升序列,并且上
    升序列的长度等于 dp[i] ,那么我们就把 count[i] 加上 count[j] 的值。这样循 环⼀遍之后, count[i] 存的就是我们想要的值。
    综上所述,对于 count[i] ,我们可以得到状态转移⽅程为:
    count[i] += count[j] ,其中 0 <= j < i ,并且 nums[j] < nums[i] && dp[j] + 1 == dp[i] 。
    初始化: ◦ 对于 len[i] ,所有元素自己就能构成⼀个上升序列,直接全部初始化为 1 ; ◦ 对于 count[i] ,如果全部初始化为 1 ,在累加的时候可能会把「不是最大长度的情况」累加进去,因此,我们可以先初始化为 0 ,然后在累加的时候判断⼀下即可。具体操作情况看代 码~
    填表顺序: 毫无疑问是「从左往右」。
    返回值: 用 manLen 表⽰最终的最长递增子序列的长度。 根据题目要求,我们应该返回所有长度等于 maxLen 的子序列的个数。

  2. 代码示例:

   public int findNumberOfLIS(int[] nums) {// 1. 创建 dp 表// 2. 初始化// 3. 填表// 4. 返回值int n = nums.length;int[] len = new int[n];int[] count = new int[n];for (int i = 0; i < n; i++) len[i] = count[i] = 1;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;}        

四、最长数对链

  1. 题目链接:最长数对链
  2. 题目描述:

给你⼀个由 n 个数对组成的数对数组 pairs ,其中 pairs[i] = [lefti, righti] 且 lefti < righti 。 现在,我们定义⼀种跟随关系,当且仅当 b < c 时,数对 p2 = [c, d] 才可以跟在 p1 = [a, b] 后⾯。我们用这种形式来构造数对链 。 找出并返回能够形成的 最长数对链的长度 。 你不需要用到所有的数对,你可以以任何顺序选择其中的⼀些数对来构造。
示例 1:
输入:pairs = [[1,2], [2,3], [3,4]]
输出:2
解释:最长的数对链是 [1,2] -> [3,4] 。
示例 2:
输入:pairs = [[1,2],[7,8],[4,5]]
输出:3
解释:最长的数对链是 [1,2] -> [4,5] -> [7,8] 。

3.解法(动态规划):
算法思路:这道题目让我们在数对数组中挑选出来⼀些数对,组成⼀个呈现上升形态的最长的数对链。像不像我们整数数组中挑选⼀些数,让这些数组成⼀个最长的上升序列?因此,我们可以把问题转化成我们之前的⼀个模型: 最长递增子序列。因此我们解决问题的⽅向,应该在「最长递增子序列」这个模型上。 不过,与整形数组有所区别。在用动态规划结局问题之前,应该先把数组排个序。因为我们在计算dp[i] 的时候,要知道所有左区间比 pairs[i] 的左区间小的链对。排完序之后,只用「往前遍历⼀遍」即可。
状态表示:dp[i] 表示以 i 位置的数对为结尾时,最长数对链的长度。
状态转移方程: 对于 dp[i] ,遍历所有 [0, i - 1] 区间内数对用 j 表⽰下标,找出所有满足pairs[j][1] < pairs[i][0] 的 j 。找出里面最大的 dp[j] ,然后加上 1 ,就是以 i 位置为结尾的最长数对链。
初始化: 刚开始的时候,全部初始化为 1 。
填表顺序: 根据「状态转移⽅程」,填表顺序应该是「从左往右」。
返回值: 根据「状态表示」,返回整个 dp 表中的最大值。

  1. 代码示例:
 public int findLongestChain(int[][] pairs) {Arrays.sort(pairs, (a, b) -> a[0] - b[0]);int n = pairs.length;int[] dp = new int[n];for(int i = 0;i<n;i++) dp[i] = 1;int ret = 1;for(int i = 0; i<n; i++){for(int j = 0; j < i;j++){if(pairs[j][1] < pairs[i][0]){dp[i] = Math.max(dp[j]+1,dp[i]);}ret = Math.max(ret,dp[i]);}}return ret;}

五、最长定差子序列

  1. 题目链接:最长定差子序列
  2. 题目描述:

给你⼀个整数数组 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 <= 10^5
-10^4 <= arr[i], difference <= 10^4

  1. 解法(动态规划): 算法思路:
    这道题和 300. 最⻓递增⼦序列 有⼀些相似,但仔细读题就会发现,本题的 arr.lenght 高达10^5 ,使用 O(N^2) 的 lcs 模型⼀定会超时。 那么,它有什么信息是 300. 最长递增子序列 的呢?是定差。之前,我们只知道要递增,不知道前 ⼀个数应当是多少;现在我们可以计算出前⼀个数是多少了,就可以用数值来定义 dp 数组的值,并形成状态转移。这样,就把已有信息有效地利用了起来。
    状态表示:dp[i] 表示:以 i 位置的元素为结尾所有的子序列中,最长的等差子序列的长度。
    状态转移⽅程: 对于 dp[i] ,上⼀个定差子序列的取值定为 arr[i] - difference 。只要找到以上⼀个数字为结尾的定差子序列长度的 dp[arr[i] - difference] ,然后加上 1 ,就是以 i 为结尾的定差子序列的⻓度。 因此,这里可以选择使用哈希表做优化。我们可以把「元素, dp[j] 」绑定,放进哈希表中。甚至不用创建 dp 数组,直接在哈希表中做动态规划。
    初始化: 刚开始的时候,需要把第一个元素放进哈希表中, hash[arr[0]] = 1 。
    填表顺序: 根据「状态转移方程」,填表顺序应该是「从左往右」。
    返回值: 根据「状态表示」,返回整个 dp 表中的最⼤值。

  2. 代码示例:

 public int longestSubsequence(int[] arr, int difference) {// 创建⼀个哈希表,在哈希表中做 dpMap<Integer, Integer> hash = new HashMap<Integer, Integer>();//arr[i], dp[i]int ret = 1;for (int a : arr) {hash.put(a, hash.getOrDefault(a - difference, 0) + 1);ret = Math.max(ret, hash.get(a));}return ret;}

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

  1. 题目链接:最长的斐波那契子序列的长度
  2. 题目描述:

如果序列 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 ,我们可以用「经验 + 题目要求」来定义状态表示:
    i. 以某个位置为结尾,巴拉巴拉;
    ii. 以某个位置为起点,巴拉巴拉。 这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义一个状态表示:
    dp[i] 表示:以 i 位置元素为结尾的「所有子序列」中,最长的斐波那契子数列的长度。 但是这里有⼀个非常致命的问题,那就是我们⽆法确定 i 结尾的斐波那契序列的样子。这样就会导 致我们无法推导状态转移方程,因此我们定义的状态表示需要能够确定⼀个斐波那契序列。 根据斐波那契数列的特性,我们仅需知道序列里面的最后两个元素,就可以确定这个序列的样⼦。 因此,我们修改我们的状态表示为:
    dp[i][j] 表示:以 i 位置以及 j 位置的元素为结尾的所有的子序列中,最长的斐波那契子序列的长度。规定⼀下 i < j 。
    状态转移方程: 设 nums[i] = b, nums[j] = c ,那么这个序列的前⼀个元素就是 a = c - b 。我们根 据 a 的情况讨论:
    i. a 存在,下标为 k ,并且 a < b :此时我们需要以 k 位置以及 i 位置元素为结尾的 最⻓斐波那契⼦序列的⻓度,然后再加上 j 位置的元素即可。于是 dp[i][j] =
    dp[k][i] + 1 ;
    ii. a 存在,但是 b < a < c :此时只能两个元素自己玩了, dp[i][j] = 2 ;
    iii. a 不存在:此时依旧只能两个元素自己玩了, dp[i][j] = 2 。 综上,状态转移⽅程分情况讨论即可。 优化点:我们发现,在状态转移方程中,我们需要确定 a 元素的下标。因此我们可以在 dp 之 前,将所有的「元素 + 下标」绑定在⼀起,放到哈希表中。
    初始化: 可以将表里面的值都初始化为 2 。
    填表顺序:
    a. 先固定最后⼀个数;
    b. 然后枚举倒数第⼆个数。
    返回值: 因为不知道最终结果以谁为结尾,因此返回 dp 表中的最大值 ret 。 但是 ret 可能小于 3 ,小于 3 的话说明不存在。 因此需要判断⼀下。

  2. 代码示例:

  public int lenLongestFibSubseq(int[] nums) {Map<Integer,Integer> hash  =new HashMap<>();int n = nums.length;for(int i = 0;i<n;i++) hash.put(nums[i] , i);int[][] dp  = new int[n][n];for(int i =0;i<n;i++){for(int j =0;j<n;j++){dp[i][j] = 2;}}int ret = 2;for(int j = 2; j< n;j++){for(int i = 1;i<j;i++){int a =nums[j] - nums[i];if(a<nums[i] && hash.containsKey(a)){dp[i][j]  = dp[hash.get(a)][i] + 1;}ret = Math.max(dp[i][j] , ret);} }return ret<3?0:ret;}

七、最长等差数列

  1. 题目链接:最长等差数列
  2. 题目描述

给你⼀个整数数组 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 ,我们可以⽤「经验 + 题目要求」来定义状态表示:
    i. 以某个位置为结尾,巴拉巴拉;
    ii. 以某个位置为起点,巴拉巴拉。 这⾥我们选择比较常用的⽅式,以某个位置为结尾,结合题目要求,定义⼀个状态表⽰:
    dp[i] 表示:以 i 位置元素为结尾的「所有子序列」中,最⻓的等差序列的长度。 但是这里有⼀个非常致命的问题,那就是我们⽆法确定 i 结尾的等差序列的样⼦。这样就会导致 我们无法推导状态转移方程,因此我们定义的状态表示需要能够确定⼀个等差序列。 根据等差序列的特性,我们仅需知道序列里面的最后两个元素,就可以确定这个序列的样子。因此,我们修改我们的状态表示为:
    dp[i][j] 表示:以 i 位置以及 j 位置的元素为结尾的所有的子序列中,最长的等差序列的长度。规定⼀下 i < j 。
    状态转移方程: 设 nums[i] = b, nums[j] = c ,那么这个序列的前⼀个元素就是 a = 2 * b - c 。我们 根据 a 的情况讨论:
    a. a 存在,下标为 k ,并且 a < b :此时我们需要以 k 位置以及 i 位置元素为结尾的最 ⻓等差序列的长度,然后再加上 j 位置的元素即可。于是 dp[i][j] = dp[k][i] + 1 。这⾥因为会有许多个 k ,我们仅需离 i 最近的 k 即可。因此任何最⻓的都可以以 k 为结尾;
    b. a 存在,但是 b < a < c :此时只能两个元素自己玩了, dp[i][j] = 2 ;
    c. a 不存在:此时依旧只能两个元素自己玩了, dp[i][j] = 2 。 综上,状态转移方程分情况讨论即可。 优化点:我们发现,在状态转移⽅程中,我们需要确定 a 元素的下标。因此我们可以将所有的元素 + 下标绑定在⼀起,放到哈希表中,这里有两种策略:
    a. 在 dp 之前,放⼊哈希表中。这是可以的,但是需要将下标形成⼀个数组放进哈希表中。这样时间复杂度较高,我帮⼤家试过了,超时。
    b. ⼀边 dp ,⼀边保存。这种方式,我们仅需保存最近的元素的下标,不用保存下标数组。但是用这种方法的话,我们在遍历顺序那里,先固定倒数第⼆个数,再遍历倒数第⼀个数。这样就可以在 i 使用完时候,将 nums[i] 扔到哈希表中。
    初始化: 根据实际情况,可以将所有位置初始化为 2 。
    填表顺序:
    a. 先固定倒数第二个数;
    b. 然后枚举倒数第⼀个数。
    返回值: 由于不知道最长的结尾在哪里,因此返回 dp 表中的最大值

  2. 代码示例:

  public int longestArithSeqLength(int[] nums) {// 优化Map<Integer, Integer> hash = new HashMap<Integer, Integer>();hash.put(nums[0], 0);// 建表int n = nums.length;int[][] dp = new int[n][n];for (int i = 0; i < n; i++) // 初始化Arrays.fill(dp[i], 2);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.containsKey(a)) {dp[i][j] = dp[hash.get(a)][i] + 1;ret = Math.max(ret, dp[i][j]);}}hash.put(nums[i], i);}return ret;}

八、等差数列划分II - 子序列

  1. 题目链接:等差数列划分II - 子序列
  2. 题目描述:

给你⼀个整数数组 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 ,我们可以⽤「经验 + 题目要求」来定义状态表示:
    i. 以某个位置为结尾,巴拉巴拉;
    ii. 以某个位置为起点,巴拉巴拉。 这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义⼀个状态表示:
    dp[i] 表示:以 i 位置元素为结尾的「所有子序列」中,等差子序列的个数。 但是这⾥有⼀个⾮常致命的问题,那就是我们⽆法确定 i 结尾的等差序列的样子。这样就会导致 我们无法推导状态转移方程,因此我们定义的状态表示需要能够确定⼀个等差序列。 根据等差序列的特性,我们仅需知道序列里面的最后两个元素,就可以确定这个序列的样子。因此,我们修改我们的状态表示为:dp[i][j] 表示:以 i 位置以及 j 位置的元素为结尾的所有的⼦序列中,等差子序列的个 数。规定⼀下 i < j 。
    状态转移⽅程: 设 nums[i] = b, nums[j] = c ,那么这个序列的前⼀个元素就是 a = 2 * b - c 。我们 根据 a 的情况讨论:
    a. a 存在,下标为 k ,并且 a < b :此时我们知道以 k 元素以及 i 元素结尾的等差序列 的个数 dp[k][i] ,在这些子序列的后⾯加上 j 位置的元素依旧是等差序列。但是这里会多 出来⼀个以 k, i, j 位置的元素组成的新的等差序列,因此 dp[i][j] = dp[k][i] + 1 ;
    b. 因为 a 可能有很多个,我们需要全部累加起来。 综上, dp[i][j] += dp[k][i] + 1 。 优化点:我们发现,在状态转移⽅程中,我们需要确定 a 元素的下标。因此我们可以在 dp 之前,将 所有元素 + 下标数组绑定在⼀起,放到哈希表中。这里为何要保存下标数组,是因为我们要统计个数,所有的下标都需要统计。
    初始化: 刚开始是没有等差数列的,因此初始化 dp 表为 0 。
    填表顺序:
    a. 先固定倒数第⼀个数;
    b. 然后枚举倒数第二个数。
    返回值: 我们要统计所有的等差子序列,因此返回 dp 表中所有元素的和。

  2. 代码示例:

  public int numberOfArithmeticSlices(int[] nums) {// 优化Map<Long, List<Integer>> hash = new HashMap<>();int n = nums.length;for (int i = 0; i < n; i++) {long tmp = (long) nums[i];if (!hash.containsKey(tmp))hash.put(tmp, new ArrayList<Integer>());hash.get(tmp).add(i);}int[][] dp = new int[n][n];int sum = 0;for (int j = 2; j < n; j++) // 固定倒数第⼀个数for (int i = 1; i < j; i++) // 枚举倒数第⼆个数{long a = 2L * nums[i] - nums[j];if (hash.containsKey(a)) {for (int k : hash.get(a))if (k < i) dp[i][j] += dp[k][i] + 1;else break; // ⼩优化}sum += dp[i][j];}return sum;}

结语

本文到这里就结束了,主要通过几道非连续子序列算法题,介绍了这种类型动态规划的做题思路,带大家深入了解了动态规划中非连续子序列这一类型。

以上就是本文全部内容,感谢各位能够看到最后,如有问题,欢迎各位大佬在评论区指正,希望大家可以有所收获!创作不易,希望大家多多支持!

最后,大家再见!祝好!我们下期见!

相关文章:

【动态规划】深入动态规划 非连续子序列问题

文章目录 前言例题一、最长递增子序列二、摆动序列三、最长递增子序列的个数四、最长数对链五、最长定差子序列六、最长的斐波那契子序列的长度七、最长等差数列八、等差数列划分II - 子序列 结语 前言 什么是动态规划中的非连续子序列问题&#xff1f; 动态规划中的非连续子序…...

灵魂拷问,指针为什么是C语言的灵魂?

目录 | 引 言 | 内存操作 | 构造复杂的数据结构 | 底层硬件交互 | 指针的陷阱 | 文 末 | 引 言 指针是C语言的灵魂&#xff01; 这句话是不是很耳熟&#xff1f;但为什么这么说&#xff0c;你知道吗&#xff1f; 本篇小文就从内存、数据结构、底层硬件交互这三个维度来…...

Node.js自定义中间件

目录 Node.js 自定义中间件详细介绍 1. 目录结构 2. 什么是自定义中间件&#xff1f; 3. 代码实现 1. 自定义日志中间件&#xff08;记录请求信息&#xff09; 2. 自定义身份验证中间件&#xff08;校验用户权限&#xff09; 3. 自定义请求时间中间件&#xff08;记录请…...

Qt 音乐播放器项目

具体代码见&#xff1a;https://gitee.com/Suinnnnnn/MusicPlayer 文章目录 0. 预备1. 界面1.1 各部位长度1.2 ui文件1.3 窗口前置设置1.4 设置QSS 2. 自定义控件2.1 按钮2.2 推荐页面2.3 CommonPage2.4 滑杆 3. 音乐管理4. 歌词界面4.1 ui文件4.2 LrcPage.h文件 5. 音乐播放控…...

初识数据结构——Java集合框架解析:List与ArrayList的完美结合

&#x1f4da; Java集合框架解析&#xff1a;List与ArrayList的完美结合 &#x1f31f; 前言&#xff1a;为什么我们需要List和ArrayList&#xff1f; 在日常开发中&#xff0c;我们经常需要处理一组数据。想象一下&#xff0c;如果你要管理一个班级的学生名单&#xff0c;或…...

LightRAG实战:轻松构建知识图谱,破解传统RAG多跳推理难题

作者&#xff1a;后端小肥肠 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; 2025防失业预警&#xff1a;不会用DeepSeek-RAG建知识库的人正在被淘汰_deepseek-embedding-CSDN博客 从PDF到精准答案&#xff1a;Coze…...

Hyperlane框架全面详解与应用指南 [特殊字符][特殊字符][特殊字符]

Hyperlane框架全面详解与应用指南 &#x1f680;&#x1f680;&#x1f680; &#x1f4da; 前言 欢迎来到Hyperlane框架的全面详解与应用指南&#xff01;&#x1f389;&#x1f389;&#x1f389; 本文档旨在为开发者提供一个全面、详尽的Hyperlane框架使用指南&#xff0c…...

使用LVS的 NAT 模式实现 3 台RS的轮询访问

题目 使用LVS的 NAT 模式实现 3 台RS的轮询访问。IP地址和主机自己规划。 -i— turn&#xff0c;-g——DR模式&#xff0c;-m——NAT模式 节点规划 仅主机网段&#xff1a;192.168.216.0/24 NAT网段&#xff1a;192.168.88.0/24 主机角色系统网络ipclientclientredhat9.5仅…...

BGP路由协议之属性4

MED 多出口鉴别器 可选非过渡属性 EBGP 的邻居 Cost 开销值&#xff0c;控制如何进入 AS。越小越优。继承 IGP 的开销值&#xff0c;默认 0 MED(Multi-Exit Discriminator&#xff0c;多出口鉴别器)是可选非过属性&#xff0c;是一种度量值用于向外部对等体指出进入本 AS 的首…...

数据库的操作

1.创建数据库 CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [,create_specification] ...]create_specification: [DEFAULT] CHARACTER SET charset_name [DEFAULT] COLLATE collation_name 大写的表示关键字。[]是可选项。CHARACTER SET&#xff1a;指定…...

【愚公系列】《高效使用DeepSeek》055-可靠性评估与提升

🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! 👉 江湖人称"愚公搬代码",用七年如一日的精神深耕技术领域,以"…...

记录clickhouse记录一次性能优化,从60s到1s

文章目录 问题表结构类似如下分析第一步调整第一步观察多磁盘读继续观察sql 问题 一个查询接口&#xff0c;涉及多个clickhouse 查询&#xff0c;查询用时一下变成要60s 表结构类似如下 CREATE TABLE demo.test_local (id UUID,date DateTime,type String ) ENGINE Replic…...

二叉树的层序遍历

102. Binary Tree Level Order Traversal 广度优先搜索 将每个结点的层号记录下。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* …...

嵌入式硬件篇---TOF陀螺仪SPI液晶屏

文章目录 前言1. TOF传感器&#xff08;Time of Flight&#xff09;原理STM32使用方法硬件连接SDASCLVCC\GND 软件配置初始化I2C外设库函数驱动&#xff1a;读取数据 2. 陀螺仪&#xff08;如MPU6050&#xff09;原理STM32使用方法硬件连接SDA/SCLINTVCC/GND 软件配置初始化I2C…...

OpenCV 在树莓派上进行实时人脸检测

这段 Python 代码借助 OpenCV 库实现了在树莓派上进行实时人脸检测的功能。它会开启摄像头捕获视频帧&#xff0c;在每一帧里检测人脸并以矩形框标记出来&#xff0c;同时在画面上显示帧率&#xff08;FPS&#xff09;。 依赖库 cv2&#xff1a;OpenCV 库&#xff0c;用于计算…...

55.跳跃游戏

​​题目来源&#xff1a; leetcode题目&#xff0c;网址&#xff1a;55. 跳跃游戏 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 遍历数组&#xff0c;若当前节点可达&#xff0c;更新可到达的最远距离&#xff0c;否则返回false。若可遍历整个数组&#xf…...

awk 实现listagg ,count 功能

awk命令实现分组统计 测试数据 ABC a1 ABC a2 ABC a3 ABD c1 ABD c2 分组统计 abc a1,a2,a3 3 abd c1,c2 awk 命令 awk {arr[$1]arr[$1] ? arr[$1] "," $2 : $2; count[$1]} END{for (i in arr) print tolower(i), arr[i], count[i]} group_…...

瑞萨RA4M2使用心得-GPIO输出

目录 一、新建项目 二、图形化开发 1.初始化IO 2.界面介绍 3.代码编写 4.所有内部函数的封装位置 5.LED闪烁函数编写 三.debug运行 总结 环境&#xff1a; 开发板&#xff1a;RA-Eco-RA4M2-100PIN-V1.0 IDE&#xff1a;e2 studio 一、新建项目 正常操作&#xff0c;下…...

uniapp微信小程序引入vant组件库

1、首先要有uniapp项目&#xff0c;根据vant官方文档使用yarn或npm安装依赖&#xff1a; 1、 yarn init 或 npm init2、 # 通过 npm 安装npm i vant/weapp -S --production# 通过 yarn 安装yarn add vant/weapp --production# 安装 0.x 版本npm i vant-weapp -S --production …...

COZE通关指南:工作流与插件开发

前言 本文隶属于专栏《AI Agent 通关指南》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见《AI Agent 通关指南》 正文 1. 平台基础介绍 🌟 1.1 COZE平台概述 COZE平台(coze.cn)是一个强大的AI应用开发平台…...

在Unity中,如果物体上的脚本丢失,可以通过编写一个自定义编辑器脚本来查找并删除这些丢失的组件

在Unity中&#xff0c;如果物体上的脚本丢失&#xff0c;可以通过编写一个自定义编辑器脚本来查找并删除这些丢失的组件。以下是一个示例脚本&#xff0c;它可以帮助你一键检索场景中所有丢失脚本的物体&#xff0c;并删除这些丢失的组件。 步骤&#xff1a; 创建编辑器脚本&a…...

青少年编程与数学 02-016 Python数据结构与算法 04课题、栈与队列

青少年编程与数学 02-016 Python数据结构与算法 04课题、栈与队列 一、栈1. 栈的定义2. 栈的特点3. 栈的基本操作示例 4. 栈的实现&#xff08;1&#xff09;数组实现&#xff08;2&#xff09;链表实现 5. 栈的应用&#xff08;1&#xff09;函数调用&#xff08;2&#xff09…...

Lucene.Net全文搜索引擎:架构解析与全流程实战指南

文章目录 引言&#xff1a;为什么选择Lucene.Net&#xff1f;一、Lucene.Net核心架构剖析1.1 模块化设计 二、Lucene.Net索引原理揭秘2.1 倒排索引&#xff1a;搜索的基石2.2 段&#xff08;Segment&#xff09;机制 三、全流程实战&#xff1a;从0到1构建搜索引擎3.1 环境准备…...

OpenSceneGraph 中的 LOD详解

LOD (Level of Detail&#xff0c;细节层次) 是3D图形中一种重要的优化技术&#xff0c;OpenSceneGraph 通过 osg::LOD 类提供了完整的LOD支持。 一、LOD 基本概念 1. 什么是LOD 核心思想&#xff1a;根据物体与相机的距离显示不同细节程度的模型 目的&#xff1a;减少远处物…...

程序化广告行业(64/89):AdX/SSP系统广告位设置全解析

程序化广告行业&#xff08;64/89&#xff09;&#xff1a;AdX/SSP系统广告位设置全解析 大家好&#xff01;我一直觉得在技术和营销不断融合的当下&#xff0c;程序化广告领域充满了机遇与挑战。之前和大家分享了程序化广告PDB模式的相关知识&#xff0c;今天想接着和大家一起…...

Pytorch中的计算图(Computational Graph)是什么

&#x1f9e9; 一、什么是计算图&#xff1f; 计算图是一种“有向无环图&#xff08;DAG&#xff09;”&#xff0c;表示变量&#xff08;张量&#xff09;之间的运算关系。 节点&#xff1a;张量或操作&#xff08;如加法、乘法&#xff09;边&#xff1a;数据流&#xff08;即…...

Java 大视界 -- Java 大数据在航天遥测数据分析中的技术突破与应用(177)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

【Linux操作系统——学习笔记三】Linux环境下多级目录构建与管理的命令行实践报告

1.在用户主目录下&#xff0c;使用以下方法新建目录&#xff0c;并显示详细执行过程&#xff1a; &#xff08;1&#xff09;使用绝对路径在当前目录下创建 new_dir目录 &#xff08;2&#xff09;使用相对路径、在当前目录创建dir1、dir2、dir3目录 &#xff08;3&#xff09…...

java.util.Collections中常用api

在Java中&#xff0c;java.util.Collections 是一个工具类&#xff0c;提供了大量静态方法用于操作或返回集合&#xff08;如List、Set、Map等&#xff09;。以下是常用的API分类整理&#xff1a; 1. 排序与顺序操作 sort(List<T> list) 对List进行自然顺序排序&#xff…...

批量将图片统一色调

from PIL import Image, ImageEnhance # 确保导入 ImageEnhance 模块 import osdef adjust_image_tone(image_path, output_path, r_weight1.0, g_weight1.0, b_weight1.0, brightness1.0):"""调整图片的色调、明暗&#xff0c;并进行去图处理。参数:image_pat…...

OCC Shape 操作

#pragma once #include <iostream> #include <string> #include <filesystem> #include <TopoDS_Shape.hxx> #include <string>class GeometryIO { public:// 加载几何模型&#xff1a;支持 .brep, .step/.stp, .iges/.igsstatic TopoDS_Shape L…...

docker的run命令 笔记250406

docker的run命令 笔记250406 Docker 的 run 命令用于创建并启动一个新的容器。它是 Docker 中最常用的命令之一&#xff0c;基本语法为&#xff1a; docker run [OPTIONS] IMAGE [COMMAND] [ARG...]常用选项&#xff08;OPTIONS&#xff09; 参数说明-d 或 --detach后台运行…...

批量将 HTML 转换为 Word/Txt/PDF 等其它格式

HTML是一种超文本标记语言&#xff0c;在进行网页编辑的时候非常常见&#xff0c;我们浏览的网站内容&#xff0c;都可以保存为 html 格式&#xff0c;如果想要将 html 格式的文档转为其它格式&#xff0c;比如 Word、PDF 或者 Txt&#xff0c;我们应该怎么做呢&#xff1f;今天…...

TPS入门DAY02 服务器篇

1.创建空白插件 2.导入在线子系统以及在线steam子系统库 MultiplayerSessions.uplugin MultiplayerSessions.Build.cs 3.创建游戏实例以及初始化会话创建流程 创建会话需要的函数&#xff0c;委托&#xff0c;委托绑定的回调&#xff0c;在线子系统接口绑定某一个委托的控制其…...

C高级,终端操作

核心要点整理 刷题作业 一、基础操作 命令行提示符结构 ubuntuubuntu:~$ 当前用户 | 连接符 | 计算机名 | 当前路径 | 用户权限 用户切换 su 用户名&#xff1a;切换用户sudo passwd 用户名&#xff1a;修改用户密码 常用指令 cd -&#xff1a;返回上一次路径ls&#xff1a;显…...

Lua语言的边缘计算

Lua语言的边缘计算探索 引言 随着物联网&#xff08;IoT&#xff09;、人工智能&#xff08;AI&#xff09;和大数据技术迅速发展&#xff0c;边缘计算作为一种分布式计算架构日益受到重视。其核心理念是将计算和数据存储资源更靠近数据源&#xff0c;以降低延迟、减轻网络负…...

RabbitMQ运维

RabbitMQ运维 一.集群1.简单介绍2.集群的作用 二.搭建集群1.多机多节点搭建步骤 2.单机单节点搭建步骤 3.宕机演示 三.仲裁队列1.简单介绍2.Raft协议Raft基本概念主节点选举选举过程 3.仲裁队列的使用 四.HAProxy负载均衡1.安装HAProxy2.HAProxy的使用 一.集群 1.简单介绍 Ra…...

【ESP32】ESP32物联网应用:MQTT控制与状态监测

ESP32物联网应用&#xff1a;MQTT控制与状态监测 引言 在物联网时代&#xff0c;远程监测和控制设备已经成为现实生活中常见的需求。本文将介绍如何使用ESP32微控制器配合MQTT协议&#xff0c;实现一个简单而强大的物联网应用&#xff1a;远程状态监测和设备控制。我们将以巴…...

如何保证RabbitMQ消息的可靠传输?

在这个图中&#xff0c;消息可能丢失的场景是1&#xff0c;2&#xff0c;3 1.在生产者将消息发送给RabbitMQ的时候&#xff0c;消息到底有没有正确的到达服务器呢&#xff0c;RabbitMQ提供了两种解决方案&#xff1a; a. 通过事务机制实现&#xff08;比较消耗性能&#xff0…...

Redis高可用

主从复制 为什么要主从复制&#xff1f; 由于数据都是存储在一台服务器上&#xff0c;如果出事就完犊子了&#xff0c;比如&#xff1a; 如果服务器发生了宕机&#xff0c;由于数据恢复是需要点时间&#xff0c;那么这个期间是无法服务新的请求的&#xff1b;如果这台服务器…...

[项目总结] 在线OJ刷题系统项目技术应用(下)

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…...

链表算法中常用操作和技巧

目 1.常用技巧 1.1.画图 1.2.添加虚拟头节点 1.3.大胆引入中间变量 1.4.快慢双指针 1.4.1判断链表是否有环 1.4.2找链表中环的入口 ​2.常用操作 2.1. 创建一个新节点 2.2.尾插 2.3.头插 1.常用技巧 1.1.画图 画图可以让一些抽象的文字语言更加形象生动 画图&#…...

MySQL基础 [二] - 数据库基础

目录 库的增删查改 查看数据库 创建数据库 删除数据库 修改数据库 认识系统编码&#xff08;字符集和校验规则&#xff09; 查看系统默认字符集以及校验规则 查看数据库支持的字符集和字符集校验规则 验证不同校验码编码的影响 校验规则对数据库的影响 数据库的备份…...

【Linux篇】基础IO - 文件描述符的引入

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;Liunx &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路&#xff01; 文章目录 一. 理解文件1.1 侠义理解1.2 广义理解1.3 文件操作的归类认知1.4 系统角度 二. 回顾C语言文件…...

13.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--Refit

在微服务架构中&#xff0c;不同服务之间经常需要相互调用以完成复杂业务流程&#xff0c;而 Refit 能让这种“跨服务调用”变得简洁又可靠。开发者只需将对外暴露的 REST 接口抽象成 C# 接口&#xff0c;并通过共享库或内部 NuGet 包在各服务中引用&#xff0c;这种契约优先的…...

C++ 并发性能优化实战:提升多线程应用的效率与稳定性

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;获得2024年博客之星荣誉证书&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开发技术&#xff0c…...

前端性能优化的全方位方案【待进一步结合项目】

以下是前端性能优化的全方位方案&#xff0c;结合代码配置和最佳实践&#xff0c;涵盖从代码编写到部署的全流程优化&#xff1a; 一、代码层面优化 1. HTML结构优化 <!-- 语义化标签减少嵌套 --> <header><nav>...</nav> </header> <main&…...

(undone) 并行计算 CS149 Lecture3 (现代多核处理器2 + ISPC编程抽象)

url: https://www.bilibili.com/video/BV1du17YfE5G?spm_id_from333.788.videopod.sections&vd_source7a1a0bc74158c6993c7355c5490fc600&p3 如上堂课&#xff0c;超线程技术通过储存不同线程的 execution context&#xff0c;能够在一个线程等待 IO 的时候低成本切换…...

DiffAD:自动驾驶的统一扩散建模方法

25年3月来自新加坡公司 Carion 和北航的论文“DiffAD: A Unified Diffusion Modeling Approach for Autonomous Driving”。 端到端自动驾驶 (E2E-AD) 已迅速成为实现完全自动驾驶的一种有前途的方法。然而&#xff0c;现有的 E2E-AD 系统通常采用传统的多任务框架&#xff0c…...

QScrollArea 内部滚动条 QSS 样式失效问题及解决方案

在使用 Qt 进行 UI 开发时,我们经常希望通过 QSS(Qt Style Sheets)自定义控件的外观,比如为 QScrollArea 的内部滚动条设置特定的样式。然而,有开发者遇到了这样的问题:在 UI 设计器中预览 QSS 显示效果正常,但程序运行时却显示为系统默认样式。经过反复测试和调试,最终…...