每日题目LeetCode 3165. 不包含相邻元素的子序列的最大和 2024/10/31

给你一个整数数组 nums 和一个二维数组 queries,其中 queries[i] = [posi, xi]

对于每个查询 i,首先将 nums[posi] 设置为 xi,然后计算查询 i 的答案,该答案为 nums 中 不包含相邻元素 的 

子序列

 的 最大 和。

返回所有查询的答案之和。

由于最终答案可能非常大,返回其对 109 + 7 取余 的结果。

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

示例 1:

输入:nums = [3,5,9], queries = [[1,-2],[0,-3]]

输出:21

解释:
执行第 1 个查询后,nums = [3,-2,9],不包含相邻元素的子序列的最大和为 3 + 9 = 12
执行第 2 个查询后,nums = [-3,-2,9],不包含相邻元素的子序列的最大和为 9 。

示例 2:

输入:nums = [0,-1], queries = [[0,-5]]

输出:0

解释:
执行第 1 个查询后,nums = [-5,-1],不包含相邻元素的子序列的最大和为 0(选择空子序列)。

提示:

  • 1 <= nums.length <= 5 * 104
  • -105 <= nums[i] <= 105
  • 1 <= queries.length <= 5 * 104
  • queries[i] == [posi, xi]
  • 0 <= posi <= nums.length - 1
  • -105 <= xi <= 105

题解:

        难题!根据题目描述,我们需要进行多次单点修改和区间查询,这种场景下,我们考虑使用线段树来解决。

typedef struct SegNode {
    long long v00, v01, v10, v11;
} SegNode;

SegNode* segNodeCreate() {
    SegNode* node = (SegNode*)malloc(sizeof(SegNode));
    node->v00 = node->v01 = node->v10 = node->v11 = 0;
    return node;
}

void setSegNode(SegNode* node, long long v) {
    node->v00 = node->v01 = node->v10 = 0;
    node->v11 = fmax(v, 0LL);
}

long long bestSegNode(SegNode* node) {
    return node->v11;
}

typedef struct SegTree {
    int n;
    SegNode** tree;
} SegTree;

SegTree* segTreeCreate(int n) {
    SegTree* tree = (SegTree*)malloc(sizeof(SegTree));
    tree->n = n;
    tree->tree = (SegNode**)malloc((n * 4 + 1) * sizeof(SegNode*));
    for (int i = 0; i < n * 4 + 1; i++) {
        tree->tree[i] = segNodeCreate();
    }
    return tree;
}

void freeSegTree(SegTree* tree) {
    for (int i = 0; i <= tree->n * 4; i++) {
        free(tree->tree[i]);
    }
    free(tree->tree);
    free(tree);
}

void initSegTree(SegTree* tree, int* nums) {
    internalInit(tree, nums, 1, 1, tree->n);
}

void updateSegTree(SegTree* tree, int x, int v) {
    internalUpdate(tree, 1, 1, tree->n, x + 1, v);
}

long long querySegTree(SegTree* tree) {
    return bestSegNode(tree->tree[1]);
}

void internalInit(SegTree* tree, int* nums, int x, int l, int r) {
    if (l == r) {
        setSegNode(tree->tree[x], nums[l - 1]);
        return;
    }
    int mid = (l + r) / 2;
    internalInit(tree, nums, x * 2, l, mid);
    internalInit(tree, nums, x * 2 + 1, mid + 1, r);
    pushup(tree, x);
}

void internalUpdate(SegTree* tree, int x, int l, int r, int pos, int v) {
    if (l > pos || r < pos) {
        return;
    }
    if (l == r) {
        setSegNode(tree->tree[x], v);
        return;
    }
    int mid = (l + r) / 2;
    internalUpdate(tree, x * 2, l, mid, pos, v);
    internalUpdate(tree, x * 2 + 1, mid + 1, r, pos, v);
    pushup(tree, x);
}

void pushup(SegTree* tree, int x) {
    int l = x * 2, r = x * 2 + 1;
    tree->tree[x]->v00 = fmax(tree->tree[l]->v00 + tree->tree[r]->v10, tree->tree[l]->v01 + tree->tree[r]->v00);
    tree->tree[x]->v01 = fmax(tree->tree[l]->v00 + tree->tree[r]->v11, tree->tree[l]->v01 + tree->tree[r]->v01);
    tree->tree[x]->v10 = fmax(tree->tree[l]->v10 + tree->tree[r]->v10, tree->tree[l]->v11 + tree->tree[r]->v00);
    tree->tree[x]->v11 = fmax(tree->tree[l]->v10 + tree->tree[r]->v11, tree->tree[l]->v11 + tree->tree[r]->v01);
}

#define MOD 1000000007

int maximumSumSubsequence(int* nums, int numsSize, int** queries, int queriesSize, int* queriesColSize) {
    SegTree* tree = segTreeCreate(numsSize);
    initSegTree(tree, nums);

    long long ans = 0;
    for (int i = 0; i < queriesSize; i++) {
        updateSegTree(tree, queries[i][0], queries[i][1]);
        ans = (ans + querySegTree(tree)) % MOD;
    }
    freeSegTree(tree);
    return (int)ans;
}

### 解题思路 LeetCode 第 674 题的目标是找到给定数组中的最长连续递增子序列的长度。此问题可以通过一次线性扫描来解决,时间复杂度为 O(n),空间复杂度可以优化到 O(1)[^1]。 #### 关键点分析 - **连续性**:题目强调的是“连续”,因此只需要比较相邻两个元素即可判断是否构成递增关系。 - **动态规划 vs 贪心算法**:虽然可以用动态规划的思想解决问题,但由于只需记录当前的最大值而无需回溯历史状态,贪心策略更为高效[^3]。 --- ### Python 实现 以下是基于贪心算法的 Python 实现: ```python class Solution: def findLengthOfLCIS(self, nums): if not nums: # 如果输入为空,则返回0 return 0 max_len = 1 # 至少有一个元素时,最小长度为1 current_len = 1 # 当前连续递增序列的长度初始化为1 for i in range(1, len(nums)): # 从第二个元素开始遍历 if nums[i] > nums[i - 1]: # 判断当前元素是否大于前一个元素 current_len += 1 # 是则增加当前长度 max_len = max(max_len, current_len) # 更新全局最大长度 else: current_len = 1 # 否则重置当前长度 return max_len # 返回最终结果 ``` 上述代码通过维护 `current_len` 和 `max_len` 来跟踪当前连续递增序列的长度以及整体的最大长度。 --- ### Java 实现 下面是等效的 Java 版本实现: ```java public class Solution { public int findLengthOfLCIS(int[] nums) { if (nums.length == 0) { // 处理边界情况 return 0; } int maxLength = 1; // 初始化最大长度 int currentLength = 1; // 初始化当前长度 for (int i = 1; i < nums.length; i++) { if (nums[i] > nums[i - 1]) { // 若满足递增条件 currentLength++; // 增加当前长度 maxLength = Math.max(maxLength, currentLength); // 更新最大长度 } else { currentLength = 1; // 满足递增条件时重新计数 } } return maxLength; // 返回结果 } } ``` 该版本逻辑与 Python 类似,但在语法上更贴近 Java 的特性[^4]。 --- ### C++ 实现 对于 C++ 用户,下面是一个高效的解决方案: ```cpp #include <vector> #include <algorithm> // 使用 std::max 函数 using namespace std; class Solution { public: int findLengthOfLCIS(vector<int>& nums) { if (nums.empty()) { // 边界处理 return 0; } int result = 1; // 结果变量 int count = 1; // 当前连续递增序列长度 for (size_t i = 1; i < nums.size(); ++i) { if (nums[i] > nums[i - 1]) { // 检查递增条件 count++; result = max(result, count); } else { count = 1; // 重置计数器 } } return result; // 返回最终结果 } }; ``` 这段代码同样遵循了单次遍历的原则,并利用标准库函数简化了一些操作。 --- ### 小结 三种语言的核心思想一致,均采用了一种简单的线性扫描方式完成任务。这种方法仅易于理解,而且性能优越,在实际应用中非常实用[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值