Medium
题目描述
给你一个整数数组 nums。
你的任务是找到 nums 的最长子序列 seq 的长度,使得连续元素之间的绝对差值形成一个非递增的整数序列。换句话说,对于 nums 的子序列 seq0, seq1, seq2, ..., seqm,需要满足 |seq1 - seq0| >= |seq2 - seq1| >= ... >= |seqm - seqm-1|。
返回这样的子序列的长度。
示例 1:
输入:nums = [16,6,3]
输出:3
解释:最长子序列是 [16, 6, 3],相邻绝对差值为 [10, 3]。
示例 2:
输入:nums = [6,5,3,4,2,1]
输出:4
解释:最长子序列是 [6, 4, 2, 1],相邻绝对差值为 [2, 2, 1]。
示例 3:
输入:nums = [10,20,10,19,10,20]
输出:5
解释:最长子序列是 [10, 20, 10, 19, 10],相邻绝对差值为 [10, 10, 9, 9]。
约束条件:
2 <= nums.length <= 10^41 <= nums[i] <= 300
提示:
- 使用动态规划
- 为每个索引和每个可能的差值存储最大答案
解题思路
解题思路
这是一道典型的动态规划问题。我们需要找到最长的子序列,使得相邻元素的绝对差值非递增。
核心思想
定义状态:dp[i][diff] 表示以 nums[i] 结尾、前一个相邻差值为 diff 的最长子序列长度。
状态转移
对于每个位置 i,我们枚举之前的所有位置 j(其中 j < i):
- 计算当前相邻差值:
curr_diff = |nums[i] - nums[j]| - 枚举所有可能的前一个差值
prev_diff - 如果
prev_diff >= curr_diff,则可以进行状态转移:dp[i][curr_diff] = max(dp[i][curr_diff], dp[j][prev_diff] + 1)
初始化和边界处理
- 初始化:每个单独的元素都可以作为长度为1的子序列开始
- 对于每对相邻元素,它们可以组成长度为2的子序列
优化考虑
由于数值范围较小(1-300),差值最大为299,可以直接用二维数组存储状态,避免使用哈希表的开销。
最终答案是所有 dp[i][diff] 中的最大值。
时间复杂度为 O(n²×D),其中 D 是最大可能差值,空间复杂度为 O(n×D)。
代码实现
class Solution {
public:
int longestSubsequence(vector<int>& nums) {
int n = nums.size();
int maxDiff = 300; // 最大可能的差值
// dp[i][diff] 表示以nums[i]结尾,前一个相邻差值为diff的最长子序列长度
vector<vector<int>> dp(n, vector<int>(maxDiff + 1, 1));
int result = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
int currDiff = abs(nums[i] - nums[j]);
// 直接从j转移到i,长度为2
dp[i][currDiff] = max(dp[i][currDiff], 2);
// 从j的所有可能差值转移
for (int prevDiff = currDiff; prevDiff <= maxDiff; prevDiff++) {
if (dp[j][prevDiff] > 1) {
dp[i][currDiff] = max(dp[i][currDiff], dp[j][prevDiff] + 1);
}
}
result = max(result, dp[i][currDiff]);
}
}
return result;
}
};
class Solution:
def longestSubsequence(self, nums: List[int]) -> int:
n = len(nums)
max_diff = 300 # 最大可能的差值
# dp[i][diff] 表示以nums[i]结尾,前一个相邻差值为diff的最长子序列长度
dp = [[1] * (max_diff + 1) for _ in range(n)]
result = 1
for i in range(1, n):
for j in range(i):
curr_diff = abs(nums[i] - nums[j])
# 直接从j转移到i,长度为2
dp[i][curr_diff] = max(dp[i][curr_diff], 2)
# 从j的所有可能差值转移
for prev_diff in range(curr_diff, max_diff + 1):
if dp[j][prev_diff] > 1:
dp[i][curr_diff] = max(dp[i][curr_diff], dp[j][prev_diff] + 1)
result = max(result, dp[i][curr_diff])
return result
public class Solution {
public int LongestSubsequence(int[] nums) {
int n = nums.Length;
int maxDiff = 300; // 最大可能的差值
// dp[i,diff] 表示以nums[i]结尾,前一个相邻差值为diff的最长子序列长度
int[,] dp = new int[n, maxDiff + 1];
// 初始化
for (int i = 0; i < n; i++) {
for (int diff = 0; diff <= maxDiff; diff++) {
dp[i, diff] = 1;
}
}
int result = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
int currDiff = Math.Abs(nums[i] - nums[j]);
// 直接从j转移到i,长度为2
dp[i, currDiff] = Math.Max(dp[i, currDiff], 2);
// 从j的所有可能差值转移
for (int prevDiff = currDiff; prevDiff <= maxDiff; prevDiff++) {
if (dp[j, prevDiff] > 1) {
dp[i, currDiff] = Math.Max(dp[i, currDiff], dp[j, prevDiff] + 1);
}
}
result = Math.Max(result, dp[i, currDiff]);
}
}
return result;
}
}
var longestSubsequence = function(nums) {
const n = nums.length;
const maxDiff = 300; // 最大可能的差值
// dp[i][diff] 表示以nums[i]结尾,前一个相邻差值为diff的最长子序列长度
const dp = Array(n).fill(null).map(() => Array(maxDiff + 1).fill(1));
let result = 1;
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
const currDiff = Math.abs(nums[i] - nums[j]);
// 直接从j转移到i,长度为2
dp[i][currDiff] = Math.max(dp[i][currDiff], 2);
// 从j的所有可能差值转移
for (let prevDiff = currDiff; prevDiff <= maxDiff; prevDiff++) {
if (dp[j][prevDiff] > 1) {
dp[i][currDiff] = Math.max(dp[i][currDiff], dp[j][prevDiff] + 1);
}
}
result = Math.max(result, dp[i][currDiff]);
}
}
return result;
};
复杂度分析
| 算法指标 | 复杂度 |
|---|---|
| 时间复杂度 | O(n² × D),其中 n 是数组长度,D 是最大可能差值(300) |
| 空间复杂度 | O(n × D) |