相关题目:
918. 环形子数组的最大和
1425. 带限制的子序列和
862. 和至少为 K 的最短子数组
from typing import List
class MaxSubarraySumCircular:
"""
918. 环形子数组的最大和
https://leetcode.cn/problems/maximum-sum-circular-subarray/description/
"""
def solution(self, nums: List[int]) -> int:
n = len(nums)
# 模拟环状的 nums 数组
preSum = [0] * (2 * n + 1)
preSum[0] = 0
# 计算环状 nums 的前缀和
for i in range(1, len(preSum)):
preSum[i] = preSum[i - 1] + nums[(i - 1) % n]
# 记录答案
maxSum = float('-inf')
# 维护一个滑动窗口,以便根据窗口中的最小值计算最大子数组和
window = []
window.append(0)
for i in range(1, len(preSum)):
maxSum = max(maxSum, preSum[i] - preSum[window[0]])
# 维护窗口的大小为 nums 数组的大小
if window and i-window[0] == n:
window.pop(0)
# push
# 维护队列 单调递增
while window and preSum[window[-1]] >= preSum[i]:
window.pop()
window.append(i)
return maxSum
class ConstrainedSubsetSum:
"""
1425. 带限制的子序列和
https://leetcode.cn/problems/constrained-subsequence-sum/description/
"""
def solution(self, nums: List[int], k: int) -> int:
n = len(nums)
# 定义:dp[i] 表示以 nums[i] 结尾的子序列的最大和
dp = [0] * n
dp[0] = nums[0]
# 单调队列辅助计算 dp[i-k..i-1] 的最大值
window = [(nums[0], 0)]
for i in range(1, n):
# 状态转移方程
if window and i - k > window[0][1]:
window.pop(0)
if window:
dp[i] = max(nums[i], window[0][0] + nums[i])
else:
dp[i] = nums[i]
# 维护队列 单调递减
while window and window[-1][0] < dp[i]:
window.pop()
window.append((dp[i], i))
# dp 数组中的最大值就是结果
res = max(dp)
return res
class ShortestSubarray:
"""
862. 和至少为 K 的最短子数组
https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k/description/
"""
def solution(self, nums: List[int], k: int) -> int:
n = len(nums)
# 看题目的数据范围,前缀和数组中元素可能非常大,所以用 long 类型
preSumArr = [0] * (n + 1)
# 计算 nums 的前缀和数组
for i in range(1, n + 1):
preSumArr[i] = preSumArr[i - 1] + nums[i - 1]
q = list()
res = float("inf")
# 开始执行滑动窗口算法框架
for i, curSum in enumerate(preSumArr):
# 收缩窗口,更新结果
while q and curSum - preSumArr[q[0]] >= k:
res = min(res, i - q.pop(0))
# 维持单调队列 单调递增
while q and preSumArr[q[-1]] >= curSum:
q.pop()
q.append(i)
return res if res != float("inf") else -1