子数组和的最大值问题|豆包MarsCode AI刷题

94 阅读2分钟

问题描述

小U面临一个有趣的挑战:他有一个整数数组,他想要知道,如果从数组中删除任意一个元素后,能得到的长度为K的子数组和的最大值是多少。如果数组恰好有K个元素,那么不需要进行删除操作。

问题理解

这个问题要求在一个整数数组中找到一个长度为K+1的子数组,使得在删除任意一个元素后,该子数组的和最大。如果数组长度恰好为K,则不进行删除操作。

关键点

  1. 子数组长度为K+1:你需要找到一个长度为K+1的子数组。
  2. 删除一个元素:你可以选择删除子数组中的最小元素,以最大化子数组的和。
  3. 最大和:目标是找到删除一个元素后,子数组和的最大值。

解题思路

题目要求必须删除一个数字(数组长度大于K),那么可以记录长度为K+1的子数组中的最小值,将数组和减去它即可,从中取最大值。

cpp
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;

int solution(int n, int k, const vector<int>& nums) {
    if(n <= k) return accumulate(nums.begin(), nums.end(), 0);
    int res = -0x3f3f3f3f;

    for(int i = 0; i + k < n; i++) {
        int v = 0x3f3f3f3f, sum = 0;

        for(int j = i; j - i <= k; j++) {
            sum += nums[j];
            v = min(v, nums[j]);

        }

        res = max(res, sum - v);

    }

    return res;
}

int main() {
    std::cout << (solution(5, 3, {2, 1, 3, -1, 4}) == 8) << std::endl;
    return 0;
}

优化

由于边遍历边操作,可以用单调队列维护区间最小值,具体操作如下:

cpp
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;

int solution(int n, int k, const vector<int> &nums) {
  if (n <= k)
    return accumulate(nums.begin(), nums.end(), 0);
  int res = -0x3f3f3f3f, hh = 0, tt = -1, sum = 0;
  vector<int> q(n);

  for (int i = 0; i < n; i++) {
    sum += nums[i];
    if (i >= k + 1)
      sum -= nums[i - k - 1];

    if (hh <= tt && q[hh] <= i - k - 1)
      hh++;
    while (hh <= tt && nums[q[tt]] >= nums[i])
      tt--;
    q[++tt] = i;
    
  
    if (i >= k)
      res = max(res, sum - nums[q[hh]]);
  }
  return res;
}

int main() {
  // Add your test cases here
  std::cout << (solution(5, 3, {2, 1, 3, -1, 4}) == 8) << std::endl;
  return 0;
}

其中q为模拟队列记录一段区间最小元素的下标,边记录边更新,从而优化时间复杂度为O(n)。这种方法利用了单调队列的特性,避免了在每个子数组中重复寻找最小值的过程,大大提高了效率。通过这种方式,我们可以在遍历数组的同时,维护一个队列,其中存储当前子数组中最小元素的索引,从而在O(1)时间内找到最小值并更新结果。这种方法不仅减少了计算量,而且提高了代码的可读性和可维护性。