D. Yet Another Yet Another Task(dp&暴力)
题意:求所有(区间和 − - −区间最大值)的最大值。
即 ( ∑ i = l r a i ) − a j , a j ≥ a i (\sum\limits_{i=l}^ra_i)-a_j,a_j\geq a_i (i=l∑rai)−aj,aj≥ai
根据题目观察,可知 a j a_j aj只能取 − 30 ∼ 30 -30\sim30 −30∼30之间61个数。因此可以想到枚举区间最大值为 − 30 ∼ 30 -30\sim30 −30∼30的答案,因为我们可以只选一个数,然后答案为 0 0 0,所以当区间最大值 a j ≤ 0 a_j\leq0 aj≤0时,我们不需要考虑,所以只需考虑 [ 1 , 30 ] [1,30] [1,30]的情况,接下来我们进行枚举区间最大值,然后用 d p dp dp计算区间最大和,再减去枚举的最大值不断更新答案即可。在 d p dp dp转移时,如果当前和小于0,我们就重新置为0,因为 a n s ≥ 0 ans\geq0 ans≥0.如果当前 a [ i ] > a j a[i]>a_j a[i]>aj就将和置为0,因为我们计算的是 m a x = a j max=a_j max=aj的答案,如果此时枚举的所有 a i < a j a_i<a_j ai<aj的话,该种情况的答案肯定会被后来的答案更新掉。
p s : ps: ps:可以标记一下数组中出现过那些 [ 1 , 30 ] [1,30] [1,30]的值,只用枚举那些即可。
时间复杂度: O ( 30 n ) O(30n) O(30n)
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
#define mst(a) memset(a,0,sizeof a)
int a[N],mx[31],n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>0)
mx[a[i]]=1;
}
int ans=0;
for(int i=1;i<=30;i++){
int sum=0,ma=0;
if(!mx[i]) continue;
for(int j=1;j<=n;j++){
if(a[j]>i){//重置
sum=0;
continue;
}
else {
sum+=a[j];
if(sum<0) sum=0;//重置
if(sum>ma) ma=sum;//找到最大区间和
}
}
ans=max(ans,ma-i);
}
printf("%d\n",ans);
return 0;
}