刷题列表页
题目概述
小C拿到了一个数组,他可以进行最多一次操作:将一个元素修改为任意给定的x。小C想知道,经过这次修改后,能够得到的连续子数组的最大和是多少。
题目修正: 必须进行一次操作,即x对答案无贡献也要进行修改
测试样例
样例1:
输入:
n = 5 ,x = 10 ,a = [5, -1, -5, -3, 2]
输出:15
样例2:
输入:
n = 2 ,x = -3 ,a = [-5, -2]
输出:-2
样例3:
输入:
n = 6 ,x = 10 ,a = [4, -2, -11, -1, 4, -1]
输出:15
前置知识
-
连续子数组: 即数组中连续的一段区间,比如有6个连续子数组:
- 属于子序列(区间中可以删除一些元素,但不改变原顺序的序列)
-
前缀和:
-
后缀和: 反向前缀和:
解题思路
-
考虑朴素解法,对于,我们要考虑计算出它前面最大的贡献和后面的最大贡献,再考虑是否要替换成
- 。当时,计算前面,后面, 当前替换
- 这个操作很慢,因为需要遍历当前的前面和后面,整体复杂度为
-
考虑优化, 计算前面和后面其实是一个重复的操作
-
我们用数组表示前缀和,即在之前的所有元素的和(不包含)
-
用数组表示后缀和, 即在之后的所有元素的和(不包含)
-
特别的,本题存在负数,当前缀和(后缀)中某一段小于0,那么对于答案没有贡献,直接改为0
-
特别的
- 当数组元素均为负数且最大值小于时,答案为
- 当小于数组所有元素,(原题意是不考虑这个情况的),需要提前将最小值换为, 替换并不影响后续操作,因为x小于所有数
核心代码
int solution(int n, int x, std::vector<int> a) {
vector<int>pre(n+1),suf(n+1);
int idx = 0;
for(int i=0;i < n;i++)
if(a[i] < a[idx]) idx = i;
if(a[idx] > x) a[idx] = x;
for(int i=1; i <= n; i++)pre[i] = max(pre[i-1] + a[i-1], 0);
for(int i=n-1; i >= 0; i--) suf[i] = max(suf[i+1] + a[i], 0);
int ans = 0;
int mx = a[0];
for(int i=0; i < n; i++) {
ans = max(ans, pre[i] + max(a[i],x) + suf[i+1]);
mx = max(mx, a[i]);
}
if(mx < 0)return max(mx, x);
return ans;
}