当前位置: 首页 > article >正文

LeetCode 3165. 不包含相邻元素的子序列的最大和

. - 力扣(LeetCode)

题目

给你一个整数数组 nums 和一个二维数组 queries(n\times 2维),其中 queries[i] = [pos_i,x_i]

对于每个查询 i,首先将 nums[pos_i] 设置为 x_i,然后计算查询 i 的答案,该答案为 nums 中 不包含相邻元素 的子序列的 最大 和。

返回所有查询的答案之和。由于最终答案可能非常大,返回其对 10^9+7 取余 的结果。

子序列 是指从另一个数组中删除一些或不删除元素而不改变剩余元素顺序得到的数组。

  • 示例 1:
    • 输入:nums = [3,5,9], queries = [[1,-2],[0,-3]]
    • 输出:21
    • 解释:
      • 执行第 i = 0 个查询,因为queris[0] = [1, -2],即pos_0=1,x_0=-2,修改nums = [3,-2,9],不包含相邻元素的和最大的子序列为[3, 9],这个子序列的和为 3 + 9 = 12
      • 执行第 i=1 个查询后,因为queris[1] = [0, -3],即pos_1=0,x_1=-3,修改nums = [-3,-2,9],不包含相邻元素的和最大的子序列为[9],这个子序列的和为9。
      • 最终返回结果(12+9) \ mod \ (10^7+9) = 21
  • 示例 2:
    • 输入:nums = [0,-1], queries = [[0,-5]]
    • 输出:0
    • 解释:执行第 1 个查询后,nums = [-5,-1],不包含相邻元素的子序列的最大和为 0(选择空子序列)。
  • 提示:
    • 1 <= nums.length <= 5 * 10^4
    • -10^5 <= nums[i] <= 10^5
    • 1 <= queries.length <= 5 * 10^4
    • queries[i] == [pos_i, x_i]
    • 0 <= pos_i <= nums.length - 1
    • -10^5 <= x_i <= 10^5

解题方案

动态规划法

查询过程可以分为两步:

  1. 更新nums, nums[pos_i] = x_i
  2. 计算更新后的nums, 不包含相邻元素的子序列的最大和。
  •  计算数组中不包含相邻运算的子序列的最大和,暴力解法是列举所有的符合条件的子序列,然后取和最大的一个。进阶解法则是用动态规划的方法。动态规划的状态转移方程如下:sum[i]=\left\{\begin{matrix} max(0, arr[0]) & when\ \ i == 0\\ max(arr[1], arr[0]) & when\ \ i == 1\\ max(arr[i-2] + arr[i], arr[i-1]) & when\ \ i\geqslant 2 \end{matrix}\right.
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

  • 时间复杂度为O(nm)
  • 空间复杂度为O(m)

线段树

每更新一个元素,就要对整个求和数组sum_sequence进行运算,这里存在着一个优化空间,因此我们考虑建立线段树(这是一种区间查询的树),只更新sum_sequence中跟pos_i相关的元素。

对于了解线段树的同学,可能看到“查询”这个词,就很容易想到线段树了。

线段树的介绍见数据结构之线段树-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

  • 建树的时间复杂度是O(n),每次查询(更新耗时)时间复杂度是O(logn), 总的时间复杂度为O(n+mlogn)

  • 空间复杂度:线段树存储占用了4n+1的空间,因此空间复杂度是O(n)

看下AI的效果

Em。。。。解法没问题,直接用了线段树。

但是,这是我刚刚用MarsCodeIDE编辑的代码呀,就这么水灵灵的吸收进去了???

不知道MarsCode是做了在线学习,还是有先检索本地的产品策略呀

 


http://www.kler.cn/a/376197.html

相关文章:

  • 终于把DETR搞懂了!Detection Transformer架构详解及使用方法说明
  • Pytorch猴痘病识别
  • 消息队列-Rabbitmq(消息发送,消息接收)
  • SQL 像英语是个善意的错误
  • SpringBoot实现zip压缩包下载
  • 如何批量裁剪图片?5个软件帮助你快速进行图片裁剪
  • nginx的基本安装与服务器配置
  • 驱动TFT-1.44寸屏(ST7735)显示器
  • 【面试】数组中 Array.forEach()、Array.map() 遍历结束后是否改变原数组
  • k8s 排查集群中故障节点
  • Jenkins面试整理-如何在 Jenkins 中使用插件?
  • 2000字搞懂Java中Lambda+方法引用简化代码(开发代码量秒缩十倍)
  • 鸿蒙ArkTS中的image组件
  • 代码随想录算法训练营第四十一天 | 01背包问题(二维),01背包问题(一维),416.分割等和子集
  • 分布式和微服务系统区别
  • SpringBoot助力大型商场应急预案自动化
  • C语言日记 2024年11月2日
  • 利士策分享,锚定未来:稳健规划人生
  • git reset 删除错误提交
  • 【Python爬虫实战】网络爬虫完整指南:HTTP/HTTPS协议与爬虫安全实践
  • 博物馆3D数字化的优势有哪些?
  • ArcGIS Pro SDK (二十)流图层
  • 【Android】初始路由框架及ARouter原理
  • 基于Matlab GUI的说话人识别测试平台
  • 一般无人机和FPV无人机的区别
  • 使用 MMDetection 实现 Pascal VOC 数据集的目标检测项目练习(二) ubuntu的下载安装