LeetCode 题解工作台

带限制的子序列和

给你一个整数数组 nums 和一个整数 k ,请你返回 非空 子序列元素和的最大值,子序列需要满足:子序列中每两个 相邻 的整数 nums[i] 和 nums[j] ,它们在原数组中的下标 i 和 j 满足 i 且 j - i 。 数组的子序列定义为:将数组中的若干个数字删除(可以删除 0 个数字)…

category

6

题型

code_blocks

5

代码语言

hub

3

相关题

当前训练重点

困难 · 状态·转移·动态规划

bolt

答案摘要

我们定义 表示以 结尾的满足条件的子序列的最大和。初始时 $f[i] = 0$,答案为 $\max_{0 \leq i \lt n} f(i)$。 我们注意到题目需要我们维护滑动窗口的最大值,这就是一个典型的单调队列应用场景。我们可以使用单调队列来优化动态规划的转移。

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 状态·转移·动态规划 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回 非空 子序列元素和的最大值,子序列需要满足:子序列中每两个 相邻 的整数 nums[i] 和 nums[j] ,它们在原数组中的下标 i 和 j 满足 i < j 且 j - i <= k

数组的子序列定义为:将数组中的若干个数字删除(可以删除 0 个数字),剩下的数字按照原本的顺序排布。

 

示例 1:

输入:nums = [10,2,-10,5,20], k = 2
输出:37
解释:子序列为 [10, 2, 5, 20] 。

示例 2:

输入:nums = [-1,-2,-3], k = 1
输出:-1
解释:子序列必须是非空的,所以我们选择最大的数字。

示例 3:

输入:nums = [10,-2,-10,-5,20], k = 2
输出:23
解释:子序列为 [10, -2, -5, 20] 。

 

提示:

  • 1 <= k <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
lightbulb

解题思路

方法一:动态规划 + 单调队列

我们定义 f[i]f[i] 表示以 nums[i]\textit{nums}[i] 结尾的满足条件的子序列的最大和。初始时 f[i]=0f[i] = 0,答案为 max0i<nf(i)\max_{0 \leq i \lt n} f(i)

我们注意到题目需要我们维护滑动窗口的最大值,这就是一个典型的单调队列应用场景。我们可以使用单调队列来优化动态规划的转移。

我们维护一个从队首到队尾单调递减的单调队列 qq,队列中存储的是下标 ii,初始时,我们将一个哨兵 00 加入队列中。

我们遍历 ii00n1n - 1,对于每个 ii,我们执行以下操作:

  • 如果队首元素 q[0]q[0] 满足 iq[0]>ki - q[0] > k,说明队首元素已经不在滑动窗口内,我们需要从队首弹出队首元素;
  • 然后,我们计算 f[i]=max(0,f[q[0]])+nums[i]f[i] = \max(0, f[q[0]]) + \textit{nums}[i],表示我们将 nums[i]\textit{nums}[i] 加入滑动窗口后的最大子序列和;
  • 接下来,我们更新答案 ans=max(ans,f[i])\textit{ans} = \max(\textit{ans}, f[i])
  • 最后,我们将 ii 加入队列尾部,并且保持队列的单调性,即如果 f[q[back]]f[i]f[q[\textit{back}]] \leq f[i],我们需要将队尾元素弹出,直到队列为空或者 f[q[back]]>f[i]f[q[\textit{back}]] > f[i]

最终答案即为 ans\textit{ans}

时间复杂度 O(n)O(n),空间复杂度 O(n)O(n)。其中 nn 为数组 nums\textit{nums} 的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
    def constrainedSubsetSum(self, nums: List[int], k: int) -> int:
        q = deque([0])
        n = len(nums)
        f = [0] * n
        ans = -inf
        for i, x in enumerate(nums):
            while i - q[0] > k:
                q.popleft()
            f[i] = max(0, f[q[0]]) + x
            ans = max(ans, f[i])
            while q and f[q[-1]] <= f[i]:
                q.pop()
            q.append(i)
        return ans
speed

复杂度分析

指标
时间O(n)
空间O(n)
psychology

面试官常问的追问

外企场景
  • question_mark

    The candidate demonstrates knowledge of dynamic programming with optimizations for sliding windows and heaps.

  • question_mark

    The candidate discusses the importance of efficient state transitions and boundary conditions in dynamic programming.

  • question_mark

    The candidate understands the trade-offs of using a heap for maintaining the maximum sum of subsequences in the context of a sliding window.

warning

常见陷阱

外企场景
  • error

    Ignoring the need to maintain a sliding window of maximum subsequence sums, leading to inefficient solutions.

  • error

    Overcomplicating the solution by using brute-force methods without taking advantage of dynamic programming and heap optimizations.

  • error

    Failing to correctly handle edge cases like negative numbers or very small arrays, which can impact the correctness of the dynamic programming solution.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Allowing for an additional constraint where the sum of subsequence elements must be greater than a given threshold.

  • arrow_right_alt

    Modifying the problem to include more complex constraints on subsequence selection, such as allowing at most two elements to be skipped.

  • arrow_right_alt

    Changing the problem to find the minimum sum instead of the maximum sum, which would require reversing the optimization direction.

help

常见问题

外企场景

带限制的子序列和题解:状态·转移·动态规划 | LeetCode #1425 困难