LeetCode 3165. 不包含相邻元素的子序列的最大和
. - 力扣(LeetCode)
题目
给你一个整数数组 nums
和一个二维数组 queries(维)
,其中 queries[i] = []
。
对于每个查询 i
,首先将 nums[]
设置为 ,然后计算查询
i
的答案,该答案为 nums
中 不包含相邻元素 的子序列的 最大 和。
返回所有查询的答案之和。由于最终答案可能非常大,返回其对 取余 的结果。
子序列 是指从另一个数组中删除一些或不删除元素而不改变剩余元素顺序得到的数组。
- 示例 1:
- 输入:nums = [3,5,9], queries = [[1,-2],[0,-3]]
- 输出:21
- 解释:
- 执行第 i = 0 个查询,因为queris[0] = [1, -2],即,修改
nums = [3,-2,9]
,不包含相邻元素的和最大的子序列为[3, 9],这个子序列的和为3 + 9 = 12
。 - 执行第 i=1 个查询后,因为queris[1] = [0, -3],即,修改
nums = [-3,-2,9]
,不包含相邻元素的和最大的子序列为[9],这个子序列的和为9。 - 最终返回结果
- 执行第 i = 0 个查询,因为queris[0] = [1, -2],即,修改
- 示例 2:
- 输入:nums = [0,-1], queries = [[0,-5]]
- 输出:0
- 解释:执行第 1 个查询后,
nums = [-5,-1]
,不包含相邻元素的子序列的最大和为 0(选择空子序列)。
- 提示:
解题方案
动态规划法
查询过程可以分为两步:
- 更新nums,
- 计算更新后的nums, 不包含相邻元素的子序列的最大和。
- 计算数组中不包含相邻运算的子序列的最大和,暴力解法是列举所有的符合条件的子序列,然后取和最大的一个。进阶解法则是用动态规划的方法。动态规划的状态转移方程如下:
class Solution:
def maximumSumSubsequence(self, nums: List[int], queries: List[List[int]]) -> int:
def getMaxSumOfSubsequence(sub_nums: List[int]) -> int:
"""计算不含相邻元素的和最大的子序列"""
if len(sub_nums) <= 2:
return max(0, max(sub_nums))
sum_sequence = [0] * len(sub_nums)
sum_sequence[0] = max(0, sub_nums[0])
sum_sequence[1] = max(0 + sub_nums[1], sum_sequence[0])
for i in range(2, len(sub_nums)):
sum_sequence[i] = max(sum_sequence[i - 2] + sub_nums[i], sum_sequence[i - 1])
return max(sum_sequence)
sum = 0
for item in queries:
nums[item[0]] = item[1]
sum += getMaxSumOfSubsequence(nums)
return sum % (10**9+7)
分析复杂度
记nums长度为m, quiries长度为 n
- 时间复杂度为
- 空间复杂度为
线段树
每更新一个元素,就要对整个求和数组sum_sequence进行运算,这里存在着一个优化空间,因此我们考虑建立线段树(这是一种区间查询的树),只更新sum_sequence中跟相关的元素。
对于了解线段树的同学,可能看到“查询”这个词,就很容易想到线段树了。
线段树的介绍见数据结构之线段树-CSDN博客
class SegNode:
def __init__(self) -> None:
self.v00 = 0 # 不包含两边边界的子数组的最大和
self.v01 = 0 # 不包含左边界的子数组的最大和(右边界可以包含,也可以不包含)
self.v10 = 0 # 不包含右边界的子数组的最大和(左边界可以包含,也可以不包含)
self.v11 = 0 # 两边界都可以包含的子数组的最大和
def set_value(self, v: int) -> None:
self.v11 = max(v, 0)
def best(self) -> int:
return self.v11
class SegTree:
def __init__(self, nums: List[int]) -> None:
self.n = len(nums)
self.nums = nums
self.nodes = [SegNode() for _ in range(4 * len(nums) + 1)]
self.build(0, 0, len(nums) - 1)
def popup(self, index):
left = 2 * index + 1
right = 2 * index + 2
left_node = self.nodes[left]
right_node = self.nodes[right]
self.nodes[index].v00 = max(left_node.v01 + right_node.v00, left_node.v00 + right_node.v10)
self.nodes[index].v01 = max(left_node.v01 + right_node.v01, left_node.v00 + right_node.v11)
self.nodes[index].v10 = max(left_node.v11 + right_node.v00, left_node.v10 + right_node.v10)
self.nodes[index].v11 = max(left_node.v11 + right_node.v01, left_node.v10 + right_node.v11)
def build(self, index, left, right) -> None:
if left == right:
self.nodes[index].set_value(self.nums[left])
return
mid = (left + right) // 2
self.build(2 * index + 1, left, mid)
self.build(2 * index + 2, mid + 1, right)
self.popup(index)
def update(self, pos: int, value: int) -> None:
def internal_update(index: int, left: int, right: int, pos: int, value: int) -> None:
if left > pos or right < pos:
return
if left == right:
self.nodes[index].set_value(value)
return
mid = (left + right) // 2
internal_update(2 * index + 1, left, mid, pos, value)
internal_update(2 * index + 2, mid + 1, right, pos, value)
self.popup(index)
internal_update(0, 0, len(self.nums) - 1, pos, value)
def query_action(self, pos, value) -> int:
self.update(pos, value)
return self.nodes[0].v11
class Solution:
def maximumSumSubsequence(self, nums: List[int], queries: List[List[int]]) -> int:
tree = SegTree(nums)
sum = 0
for query in queries:
sum += tree.query_action(query[0], query[1])
return sum % (10**9+7)
分析复杂度
记nums数组长度为n, queries长度为m
-
建树的时间复杂度是,每次查询(更新耗时)时间复杂度是, 总的时间复杂度为
-
空间复杂度:线段树存储占用了4n+1的空间,因此空间复杂度是
看下AI的效果
Em。。。。解法没问题,直接用了线段树。
但是,这是我刚刚用MarsCodeIDE编辑的代码呀,就这么水灵灵的吸收进去了???
不知道MarsCode是做了在线学习,还是有先检索本地的产品策略呀