题目链接
题目大意
选一个区间 [ l , r ] [l,r] [l,r],使得 ∑ l ≤ i ≤ r a i − max l ≤ i ≤ r a i \sum \limits_{l \le i \le r} a_i - \max \limits_{l \le i \le r} a_i l≤i≤r∑ai−l≤i≤rmaxai最大。
题目分析
自己的分析:
自然的想法就是枚举最大值
a
i
a_i
ai,然后看看最大值能延伸的最左边和最右边(单调栈解决),最后再用某数据结构得到区间内的最大子段和。可惜这个数据结构我不知道怎么维护(网上好像用线段树解决的,点这里),然后瞄了一下
a
i
a_i
ai的范围,可能要用桶排序,但是用了后维护区间很麻烦,然后就不知道怎么做了。。。
正解:
首先解决:给出数组
a
a
a,计算最大区间和
∑
l
≤
i
≤
r
a
i
\sum \limits_{l \le i \le r} a_i
l≤i≤r∑ai,允许的时间复杂度为
O
(
n
)
O(n)
O(n)
方法:
d
p
dp
dp,设
d
p
i
dp_i
dpi为:以
i
i
i结尾的最大区间和,那么,转移方程为:
d
p
i
=
m
a
x
(
d
p
i
−
1
+
a
i
,
a
i
)
=
m
a
x
(
d
p
i
−
1
,
0
)
+
a
i
dp_i=max(dp_{i-1}+a_i,a_i)=max(dp_{i-1}, 0)+a_i
dpi=max(dpi−1+ai,ai)=max(dpi−1,0)+ai
接下来,因为
a
i
a_i
ai的范围很小,所以考虑枚举最大值
m
x
(
−
30
≤
m
x
≤
30
)
mx(-30 \le mx \le 30)
mx(−30≤mx≤30):
对于数组
a
a
a,我们可以把大于
m
x
mx
mx的
a
i
a_i
ai,赋值为无穷小(作用:保证最大值为
m
x
mx
mx),然后对数组
a
a
a,计算一次最大区间和,得到的结果为
s
u
m
sum
sum。最后,用
s
u
m
−
m
x
sum-mx
sum−mx去更新答案。注意
s
u
m
=
0
sum=0
sum=0时要特殊处理。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int inf = 0x3f3f3f3f;
int n;
int a[maxn];
int t[maxn];
int dp[maxn];
int max_sum() {
int ans = 0;
for(int i = 1; i <= n; i++) {
dp[i] = max(dp[i-1], 0) + t[i];
ans = max(ans, dp[i]);
}
return ans;
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
int ans = 0;
for(int mx = -30; mx <= 30; mx++) {
for(int i = 1; i <= n; i++) t[i] = a[i];
for(int i = 1; i <= n; i++) if(t[i] > mx) t[i] = -inf;
int sum = max_sum();
if(sum) ans = max(ans, sum-mx);
}
printf("%d\n", ans);
return 0;
}