动态规划5.0

动态规划 - - - 子序列问题(数组中不连续的一段)1. 最长递增子序列2. 摆动序列3. 最长递增子序列的个数4. 最长数对链5. 最长定差子序列6. 最长的斐波那契子序列的长度7. 最长等差数列8. 等差数列划分Ⅱ - 子序列

动态规划 - - - 子序列问题(数组中不连续的一段)

1. 最长递增子序列

题目链接 -> Leetcode -300.最长递增子序列

Leetcode -300.最长递增子序列

题目:给你一个整数数组 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 <= 250010^4 <= nums[i] <= 10^4

思路:

dp[i] 表示:以 i 位置元素为结尾的「所有子序列」中,最长递增子序列的长度;状态转移方程:对于 dp[i] ,我们可以根据「子序列的构成方式」,进行分类讨论:

子序列长度为 1 :只能自己了,此时 dp[i] = 1 ;子序列长度大于 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 表里面的「最大值」;

代码如下:

class Solution {

public:

int lengthOfLIS(vector& nums)

{

// 动态规划

// dp[i] 表示:以 i 位置元素为结尾的「所有子序列」中,最长递增子序列的长度

vector dp(nums.size(), 1);

int ret = 0;

for (int i = nums.size() - 1; i >= 0; i--)

{

for (int j = i + 1; j < nums.size(); j++)

{

if (nums[j] > nums[i])

{

dp[i] = max(dp[j] + 1, dp[i]);

}

}

ret = max(dp[i], ret);

}

return ret;

}

};

2. 摆动序列

题目链接 -> Leetcode -376.摆动序列

Leetcode -376.摆动序列

题目:如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如,[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 <= nums.length <= 10000 <= nums[i] <= 1000

思路:

状态表示:这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义一个状态表示: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;这种情况不需要处理,我们在初始化的时候将 dp 表初始化为 1 即可 ; ii. 子序列长度大于 1 :因为结尾要呈现上升趋势,因此需要 nums[j] < nums[i] 。在满足这个条件下, j 结尾需要呈现下降状态,最长的摆动序列就是 g[j] + 1 。因此我们要找出所有满足条件下的最大的 g[j] + 1 。

综上,因为题目需要返回的是最长子序列的长度所以 f[i] 的状态转移方程为 f[i] = max(g[j] + 1, f[i]) ,注意使用 g[j] 时需要判断;

对于 g[i] ,我们可以根据「子序列的构成方式」,进行分类讨论: i. 子序列长度为 1 :只能自己了,此时 g[i] = 1 ;这种情况不需要处理,我们在初始化的时候将 dp 表初始化为 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 表里面的最大值」,我们可以在填表的时候,顺便更新⼀个「最大值」;

代码如下:

class Solution {

public:

int wiggleMaxLength(vector& nums)

{

int n = nums.size();

// f 表存最后呈现“上升”趋势的最长摆动子序列的长度

// g 表存最后呈现“下降”趋势的最长摆动子序列的长度

vector f(n, 1), g(n, 1);

int ans = 1;

for(int i = 1; i < n; i++)

{

for(int j = i - 1; j >= 0; j--)

{

// 如果是上升趋势,f[i] 就是 g 表中的最长摆动子序列的长度 + 1,因为 g 表是呈下降趋势的;下同

if(nums[i] > nums[j]) f[i] = max(g[j] + 1, f[i]);

else if(nums[i] < nums[j]) g[i] = max(f[j] + 1, g[i]);

}

ans = max(g[i], max(f[i], ans));

}

return ans;

}

};

3. 最长递增子序列的个数

题目链接 -> Leetcode -673.最长递增子序列的个数

Leetcode -673.最长递增子序列的个数

题目:给定一个未排序的整数数组 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 <= nums.length <= 2000-10^6 <= nums[i] <= 10^6

思路:

状态表示:先尝试定义一个状态:以 i 为结尾的最长递增子序列的「个数」。那么问题就来了,我们都不知道以 i 为结尾的最长递增子序列的「长度」是多少,我怎么知道最长递增子序列的个数呢?

因此,我们解决这个问题需要两个状态,一个是「长度」,一个是「个数」:

len[i] 表示:以 i 为结尾的最长递增子序列的长度;count[i] 表示:以 i 为结尾的最长递增子序列的个数;

状态转移方程:求个数之前,我们得先知道长度,因此先看 len[i] :

在求 i 结尾的最长递增序列的长度时,我们已经知道 [0, i - 1] 区间上的 len[j] 信息,用 j 表示 [0, i - 1] 区间上的下标;我们需要的是递增序列,因此 [0, i - 1] 区间上的 nums[j] 只要能和 nums[i] 构成上升序列,那么就可以更新 dp[i] 的值,此时最长长度为 dp[j] + 1 ;我们要的是 [0, i - 1] 区间上所有情况下的最大值。

综上所述,对于 len[i] ,我们可以得到状态转移方程为:

len[i] = max(len[j] + 1, len[i]) ,其中 0 <= j < i ,并且 nums[j] < nums[i];

在知道每一个位置结尾的最长递增子序列的长度时,我们来看看能否得到 count[i] :

我们此时已经知道 len[i] 的信息,还知道 [0, i - 1] 区间上的 count[j] 信息,用 j 表示 [0, i - 1] 区间上的下标;我们可以再遍历⼀遍 [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] ;

返回值:用 retlen 表示最终的最长递增子序列的长度。根据题目要求,我们应该返回所有长度等于 retlen 的子序列的个数;

代码如下:

class Solution {

public:

int findNumberOfLIS(vector& nums)

{

int n = nums.size();

// len[i] 表示以 i 位置结尾的最长递增子序列的长度

// count[i] 表示以 i 位置结尾的最长递增子序列的个数

vector len(n, 1), count(n, 1);

int retlen = 1, retcount = 1;

for (int i = 1; i < n; i++)

{

for (int j = i - 1; j >= 0; j--)

{

if (nums[i] > nums[j])

{

// 贪心

if (len[j] + 1 > len[i])

{

len[i] = len[j] + 1;

count[i] = count[j];

}

else if (len[j] + 1 == len[i])

{

count[i] += count[j];

}

}

}

// 贪心

if (retlen == len[i])

{

retcount += count[i];

}

else if (len[i] > retlen)

{

retlen = len[i];

retcount = count[i];

}

}

return retcount;

}

};

4. 最长数对链

题目链接 -> Leetcode -646.最长数对链

Leetcode -646.最长数对链

题目:给你一个由 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] 。

提示:

n == pairs.length1 <= n <= 1000-1000 <= lefti < righti <= 1000

思路:本题思路与第一题 最长递增子序列 的思路类似,我们只需要将数对按照第一个元素排一下序即可处理;

代码如下:

class Solution {

public:

int findLongestChain(vector>& pairs)

{

sort(pairs.begin(), pairs.end()); // 对第一个元素排序

int n = pairs.size();

vector dp(n, 1);

int ans = 1;

// dp[i] 表⽰以 i 位置的数对为结尾时,最⻓数对链的⻓度

for(int i = 1; i < n; i++)

{

for(int j = i - 1; j >= 0; j--)

{

if(pairs[i][0] > pairs[j][1]) dp[i] = max(dp[j] + 1, dp[i]);

}

ans = max(dp[i], ans);

}

return ans;

}

};

5. 最长定差子序列

题目链接 -> Leetcode -1218.最长定差子序列

Leetcode -1218.最长定差子序列

题目:给你一个整数数组 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

思路:

状态表示:

dp[i] 表示:以 i 位置的元素为结尾所有的子序列中,最长的等差子序列的长度。

状态转移方程:对于 dp[i] ,上一个定差子序列的取值定为 arr[i] - difference 。只要找到以上一个数字为结尾的定差子序列长度的 dp[arr[i] - difference] ,然后加上 1 ,就是以 i 为结尾的定差子序列的长度。

因此,这里可以选择使用哈希表做优化。我们可以把「元素, dp[j] 」绑定,放进哈希表中。甚至不用创建 dp 数组,直接在哈希表中做动态规划。

返回值:根据「状态表示」,返回整个 dp 表中的最大值;

代码如下:

class Solution {

public:

int longestSubsequence(vector& arr, int difference)

{

// dp[i] 表示:以 i 位置的元素为结尾所有的⼦序列中,最长的等差子序列的长度

unordered_map hash; // arr[i], dp[i]

hash[arr[0]] = 1;

int n = arr.size();

int ans = 1;

for(int i = 1; i < n; i++)

{

hash[arr[i]] = hash[arr[i] - difference] + 1;

ans = max(hash[arr[i]], ans);

}

return ans;

}

};

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

题目链接 -> Leetcode -873.最长的斐波那契子序列的长度

Leetcode -873.最长的斐波那契子序列的长度

题目:如果序列 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 <= 10001 <= arr[i] < arr[i + 1] <= 10^9

思路:

状态表示:这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义一个状态表示:dp[i] 表示:以 i 位置元素为结尾的「所有子序列」中,最长的斐波那契子数列的长度。

但是这里有一个非常致命的问题,那就是我们无法确定 i 结尾的斐波那契序列的样子。这样就会导致我们无法推导状态转移方程,因此我们定义的状态表示需要能够确定一个斐波那契序列。

根据斐波那契数列的特性,我们仅需知道序列里面的最后两个元素,就可以确定这个序列的样子。因此,我们修改我们的状态表示为:

dp[i][j] 表示:以 i 位置以及 j 位置的元素为结尾的所有的子序列中,最长的斐波那契子序列的长度。规定: i < j.

状态转移方程:设 nums[i] = b, nums[j] = c ,那么这个序列的前一个元素就是 a = c - b 。我们根据 a 的情况讨论:

a 存在,下标为 k ,并且 a < b :此时我们需要以 k 位置以及 i 位置元素为结尾的最长斐波那契子序列的长度,然后再加上 j 位置的元素即可。于是 dp[i][j] = dp[k][i] + 1 ;a 存在,但是 b < a < c :此时只能两个元素自己了, dp[i][j] = 2 ;a 不存在:此时依旧只能两个元素自己了, dp[i][j] = 2 ;

综上,状态转移方程分情况讨论即可。

优化点:我们发现,在状态转移方程中,我们需要确定 a 元素的下标。因此我们可以在 dp 之前,将所有的「元素 + 下标」绑定在一起,放到哈希表中。

返回值:因为不知道最终结果以谁为结尾,因此返回 dp 表中的最大值 ret ;但是 ret 可能小于 3 ,小于 3 的话说明不存在;因此需要判断一下。

代码如下:

class Solution {

public:

int lenLongestFibSubseq(vector& arr)

{

int n = arr.size();

unordered_map hash; // arr[i] - i ,将数组中的元素与下标绑定放入哈希表中

for (int i = 0; i < n; i++) hash[arr[i]] = i;

vector> dp(n, vector(n, 2));

int ans = 2;

// dp[j][i] 表示以 j 和 i 位置的元素为结尾的所有子序列中,最长的斐波那契子序列的长度

for (int i = 2; i < n; i++) // 固定 i 在后

{

for (int j = i - 1; j >= 1; j--) //固定 j 在前

{

int tp = arr[i] - arr[j];

if (tp < arr[j] && hash.count(tp)) dp[j][i] = dp[hash[tp]][j] + 1;

ans = max(dp[j][i], ans);

}

hash[arr[i]] = i;

}

return ans < 3 ? 0 : ans;

}

};

7. 最长等差数列

题目链接 -> Leetcode -1027.最长等差数列

Leetcode -1027.最长等差数列

题目:给你一个整数数组 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 <= 10000 <= nums[i] <= 500

思路:本题思路与上题思路类似,只是在推前一个元素的方法不一样,其中本题:设 nums[i] = b, nums[j] = c ,那么这个序列的前一个元素就是 a = 2 * b - c ;接下来我们和上题根据 a 的情况讨论,这里就不再做讨论。

代码如下:

class Solution {

public:

int longestArithSeqLength(vector& nums)

{

int n = nums.size();

unordered_map hash; // nums[i], i

//for(int i = 0; i < n; i++) hash[nums[i]] = i;

hash[nums[0]] = 0;

int ans = 2;

vector> dp(n, vector(n, 2));

// dp[i][j] 表示:以 i 位置以及 j 位置的元素为结尾的所有的⼦序列中,最长的等差序列的⻓度。规定 i < j

for(int i = 1; i < n; i++) // 固定倒数第二个数

{

for(int j = i + 1; j < n; j++) // 固定倒数第一个数

{

// 求出以 i 和 j 位置结尾的子序列中,以它们为等差数列的前一个数

int tp = 2 * nums[i] - nums[j];

if(hash.count(tp))

{

dp[i][j] = dp[hash[tp]][i] + 1;

}

ans = max(ans, dp[i][j]);

}

// 一边做 dp 一边更新hash数组;以便多个 tp 元素重复时,hash[tp]找到的是离 i 最近的下标

hash[nums[i]] = i;

}

return ans;

}

};

8. 等差数列划分Ⅱ - 子序列

题目链接 -> 等差数列划分Ⅱ - 子序列

Leetcode -446.等差数列划分Ⅱ - 子序列

题目:给你一个整数数组 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-2^31 <= nums[i] <= 2^31 - 1

思路:

本题与最长等差数列的区别是,最长等差数列求的是最长等差数列的长度,本题求的是等差数列的数目;所以思路我们也是类似的:

状态表示:dp[i][j] 表示:以 i 位置以及 j 位置的元素为结尾的所有的子序列中,等差子序列的个数。规定⼀下 i < j. 状态转移方程:设 nums[i] = b, nums[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 可能有很多个,我们需要全部累加起来。

综上, dp[i][j] += dp[k][i] + 1.

返回值:我们要统计所有的等差子序列,因此返回 dp 表中所有元素的和。

代码如下:

class Solution {

public:

int numberOfArithmeticSlices(vector& nums)

{

int n = nums.size();

// 将数组的元素和下标数组绑定在一起放入哈希表中,因为相同的元素会有不同的下标

unordered_map> hash;

for(int i = 0; i < n; i++) hash[nums[i]].push_back(i);

vector> dp(n, vector(n));

int count = 0;

for(int i = 1; i < n; i++) // 枚举倒数第二个数

{

for(int j = i + 1; j < n; j++) // 枚举倒数第一个数

{

long long tp = (long long)2 * nums[i] - nums[j];

if(hash.count(tp))

{

// 遍历 tp 元素的下标数组,tp 的下标需要小于 i

for(auto& k : hash[tp])

{

// 需要 += ,是因为要确定以 i、j 为结尾的元素前面的等差数列个数,就要用以 k、i 为结尾的元素前面的等差数列个数再加上1;因为以 k、i 为结尾的元素前面的等差数列再加上 j 的元素,也能构成数量相同的等差数列个数,而且多了 k、i、j 这一组等差数列,所以要多加1

if(k < i) dp[i][j] += dp[k][i] + 1;

}

}

count += dp[i][j];

}

}

return count;

}

};

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: