题目链接:here
题解:对于不带限制的最大字段和我们可以:求一遍前缀和,求出最大值最小值,最后结果 res = max( MAX, SUM - MIN );
那么对于这道题相当于带了限制:限制最大子段的长度是len:我们可以维护一个前缀和 ,然后结果就是 max( sum[i] - min(sum[j]) ), i-len <= j <= i-1 && 1<=i <= 2*n,注意这里j>=i-len,而不是i-len+1,因为前缀和相减的时候要注意会把j位置上的那个数也减去)。然后这么看来 是n^2的dp,那么如何优化呢。
可以预处理 长度为len的区间的最小值 然后对于每个i 查询前面区间长度为 len 的最小值 然后 sum[i] - sum[j]即可
我们可以维护一个单调队列,单调队列要满足两点:
- 队内元素的位置 要符合 (i)的区间要求 即 i-len <=que[j]<=i-1
- 单调性,队列内的元素 尽可能小(因为我们要减去最小值呀),维护一个单调非递减队列(递增或持平)
AC_Code:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 1e6+10; 5 const ll inf = 0x3f3f3f3f3f3f3f3f; 6 deque<ll>q; 7 ll a[maxn]; 8 ll n,m; 9 ll sum[maxn]; 10 11 int main() 12 { 13 scanf("%lld",&n); 14 for(ll i=1;i<=n;i++){ 15 scanf("%lld",&a[i]); 16 sum[i] = sum[i-1]+a[i]; 17 } 18 for(ll i=n+1;i<=2*n;i++){ 19 a[i] = a[i-n]; 20 sum[i] = sum[i-1]+a[i]; 21 } 22 m = n; 23 n<<=1; 24 25 while( !q.empty() ) q.pop_front(); 26 ll res = 0; 27 q.push_back(0); 28 for(ll i=1;i<=n;i++){ 29 while( !q.empty() && q.front()<i-m ) q.pop_front(); 30 res = max(res,sum[i]-sum[q.front()]); 31 while( !q.empty() && sum[q.back()]>=sum[i] ) q.pop_back(); 32 q.push_back(i); 33 } 34 printf("%lld ",res); 35 return 0; 36 }
参考博客:here