问题描述
小U面临一个有趣的挑战:他有一个整数数组,他想要知道,如果从数组中删除任意一个元素后,能得到的长度为K的子数组和的最大值是多少。如果数组恰好有K个元素,那么不需要进行删除操作。
问题理解
这个问题要求在一个整数数组中找到一个长度为K+1的子数组,使得在删除任意一个元素后,该子数组的和最大。如果数组长度恰好为K,则不进行删除操作。
关键点
- 子数组长度为K+1:你需要找到一个长度为K+1的子数组。
- 删除一个元素:你可以选择删除子数组中的最小元素,以最大化子数组的和。
- 最大和:目标是找到删除一个元素后,子数组和的最大值。
解题思路
题目要求必须删除一个数字(数组长度大于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)时间内找到最小值并更新结果。这种方法不仅减少了计算量,而且提高了代码的可读性和可维护性。