2025-07-16:最长相邻绝对差递减子序列。用go语言,给定一个整数数组 nums,需要找出其中的一个最长子序列 seq,满足该子序列相邻元素间的绝对差值构成一个非递增序列。也就是说,对于子序列中

2025-07-16:最长相邻绝对差递减子序列。用go语言,给定一个整数数组 nums,需要找出其中的一个最长子序列 seq,满足该子序列相邻元素间的绝对差值构成一个非递增序列。也就是说,对于子序列中的元素 seq0, seq1, seq2, …, seqm,要求绝对差 |seq1 - seq0| 不小于 |seq2 - seq1|,|seq2 - seq1| 不小于 |seq3 - seq2|,依此类推。

目标是求出满足上述条件的最长子序列的长度。

2 <= nums.length <= 10000。

1 <= nums[i] <= 300。

输入:nums = [16,6,3]。

输出:3。

解释:

最长子序列是 [16, 6, 3] ,相邻绝对差值为 [10, 3] 。

题目来自力扣3409。

解决思路

动态规划

动态规划是解决最长子序列问题的常见方法。我们需要设计一个状态表示和状态转移方程来高效地计算最长子序列的长度。

状态定义

定义 f[x][d] 表示以数字 x 结尾,且前一个数字与 x 的绝对差为 d 的最长子序列的长度。这里的 d 是当前子序列中最后两个元素的绝对差。

状态转移

对于数组中的每个数字 x,我们需要考虑所有可能的 d(即 x 可能与前面某个数字 y 的差为 d),并更新 f[x][d] 的值。具体步骤如下:

  1. 初始化 f 为一个二维数组,大小为 (max_num + 1) x (max_diff + 1),其中 max_num 是数组中的最大值,max_diff 是数组中最大可能的差值(即 max_num - min_num)。
  2. 对于数组中的每个数字 x,初始化 fx 为 1(因为 x 本身可以作为一个子序列)。
  3. 对于每个可能的差值 d(从 max_diff 到 0):
    • 如果存在 y = x - dy 在合法范围内(即 y >= 0),则可以尝试从 f[y][d] 转移过来,即 fx = max(fx, f[y][d] + 1)
    • 如果存在 y = x + dy 在合法范围内(即 y <= max_num),则可以尝试从 f[y][d] 转移过来,即 fx = max(fx, f[y][d] + 1)
    • 更新 f[x][d] = fx,并更新全局最大值 ans
  4. 最终 ans 就是最长满足条件的子序列的长度。

为什么从 max_diff 到 0 遍历 d

因为我们需要保证非递增的差值序列。从大到小遍历 d 可以确保在更新 f[x][d] 时,d 是当前子序列的最后两个元素的差值,而前面的差值必须大于或等于 d。这样可以自然地满足非递增的条件。

具体步骤

  1. 计算 max_nummin_num,得到 max_diff = max_num - min_num
  2. 初始化二维数组 f,大小为 (max_num + 1) x (max_diff + 1),所有元素初始为 0。
  3. 初始化 ans = 0
  4. 对于 nums 中的每个数字 x
    • 初始化 fx = 1
    • 对于 dmax_diff 到 0:
      • 如果 x - d >= 0,则 fx = max(fx, f[x - d][d] + 1)
      • 如果 x + d <= max_num,则 fx = max(fx, f[x + d][d] + 1)
      • 更新 f[x][d] = fx
      • 更新 ans = max(ans, fx)
  5. 返回 ans

时间复杂度和空间复杂度

时间复杂度

  • 外层循环遍历 nums,共 n 次(n 是数组长度)。
  • 内层循环遍历 d,共 max_diff + 1 次(max_diffmax_num - min_num)。
  • 每次内层循环的操作是常数时间。
  • 因此,总时间复杂度为 O(n * max_diff)
    • 由于 max_nummin_num 的约束(nums[i] <= 300),max_diff 最大为 300 - 1 = 299
    • 因此,时间复杂度可以视为 O(n * 300) = O(n)(因为 300 是常数)。

空间复杂度

  • 二维数组 f 的大小为 (max_num + 1) x (max_diff + 1)
    • max_num + 1 最大为 301(因为 nums[i] <= 300)。
    • max_diff + 1 最大为 300(因为 max_diff <= 299)。
  • 因此,空间复杂度为 O(300 * 300) = O(1)(常数空间)。

示例运行

nums = [16, 6, 3] 为例:

  • max_num = 16min_num = 3max_diff = 13
  • 初始化 f(17) x (14) 的二维数组(初始全 0)。
  • 遍历 nums
    • x = 16
      • fx = 1
      • 遍历 d 从 13 到 0:
        • 对于每个 df[16][d] = 1
      • ans = 1
    • x = 6
      • fx = 1
      • 遍历 d 从 13 到 0:
        • d = 10
          • y = 6 - 10 = -4(不合法)。
          • y = 6 + 10 = 16(合法),f[16][10] = 1,因此 fx = max(1, 1 + 1) = 2
        • 其他 d 不会更新 fx
        • f[6][d] 更新为 fx(对于 d = 10 是 2,其他是 1)。
      • ans = 2
    • x = 3
      • fx = 1
      • 遍历 d 从 13 到 0:
        • d = 3
          • y = 3 - 3 = 0(合法,但 f[0][3] 未更新过,默认为 0)。
          • y = 3 + 3 = 6(合法),f[6][3] = 1,因此 fx = max(1, 1 + 1) = 2
        • d = 7
          • y = 3 - 7 = -4(不合法)。
          • y = 3 + 7 = 10(合法,但 f[10][7] 未更新过,默认为 0)。
        • d = 10
          • y = 3 - 10 = -7(不合法)。
          • y = 3 + 10 = 13(合法,但 f[13][10] 未更新过,默认为 0)。
        • d = 13
          • y = 3 - 13 = -10(不合法)。
          • y = 3 + 13 = 16(合法),f[16][13] = 1,因此 fx = max(1, 1 + 1) = 2
        • 其他 d 不会更新 fx
        • f[3][d] 更新为 fx(对于 d = 3 是 2,其他是 1)。
      • ans 仍为 2(但实际应为 3,这里可能需要更细致的分析)。
  • 实际正确运行:
    • 对于 x = 3d = 3
      • y = 6f[6][3] 应为 2,因为 6 可以和 16 形成差值 10,然后 36 形成差值 3)。
      • 因此 fx = max(1, 2 + 1) = 3
    • ans = 3

总结

  • 动态规划的状态设计是关键,通过 f[x][d] 记录以 x 结尾且前一个差为 d 的最长子序列长度。
  • 通过从大到小遍历 d 确保差值的非递增性。
  • 时间复杂度和空间复杂度均为线性(因为 max_diffmax_num 是常数)。

Go完整代码如下:

package main

import (
	"fmt"
	"slices"
)

func longestSubsequence(nums []int) (ans int) {
	mx := slices.Max(nums)
	maxD := mx - slices.Min(nums)
	f := make([][]int, mx+1)
	for i := range f {
		f[i] = make([]int, maxD+1)
	}

	for _, x := range nums {
		fx := 1
		for j := maxD; j >= 0; j-- {
			if x-j >= 0 {
				fx = max(fx, f[x-j][j]+1)
			}
			if x+j <= mx {
				fx = max(fx, f[x+j][j]+1)
			}
			f[x][j] = fx
			ans = max(ans, fx)
		}
	}
	return
}

func main() {
	nums := []int{16,6,3}
	result := longestSubsequence(nums)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

# -*-coding:utf-8-*-

def longest_subsequence(nums):
    mx = max(nums)
    mn = min(nums)
    maxD = mx - mn
    # 创建二维数组 f,大小为 (mx+1) x (maxD+1),初始化为0
    f = [[0] * (maxD + 1) for _ in range(mx + 1)]

    ans = 0
    for x in nums:
        fx = 1
        for j in range(maxD, -1, -1):
            if x - j >= 0:
                fx = max(fx, f[x - j][j] + 1)
            if x + j <= mx:
                fx = max(fx, f[x + j][j] + 1)
            f[x][j] = fx
            ans = max(ans, fx)
    return ans


if __name__ == "__main__":
    nums = [16, 6, 3]
    result = longest_subsequence(nums)
    print(result)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

福大大架构师每日一题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值