题意:
N个整数组成的循环序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续的子段和的最大值(循环序列是指n个数围成一个圈,因此需要考虑a[n-1],a[n],a[1],a[2]这样的序列)。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数 (-10^9 <= S[i] <= 10^9)
Output
输出循环数组的最大子段和。
Input示例
6 -2 11 -4 13 -5 -2
Output示例
20
思路:
题目其实就是求出下标i,j,使得sum[j]-sum[i]最大,只要枚举每个j然后找到符合的最小的sum[i]就可以。这里既然是循环的数组,就需要扩充两倍长度,但是所求的结果显然不能j-i超过n,所以考虑单调队列,front端到back端保证单调sum[x]单调递增。每次枚举先把超过长度的pop掉,然后更新,再维护单调性地插入sum[i]即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e4 + 10;
ll a[MAXN * 2], sum[MAXN * 2];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%I64d", &a[i]);
a[n + i] = a[i];
}
sum[0] = 0;
for (int i = 1; i <= 2 * n; i++)
sum[i] = sum[i - 1] + a[i];
ll ans = 0;
deque <int> que;
que.push_back(0);
for (int i = 1; i <= 2 * n; i++) {
while (!que.empty() && i - que.front() > n) que.pop_front();
ans = max(ans, sum[i] - sum[que.front()]);
while (!que.empty() && sum[i] < sum[que.back()]) que.pop_back();
que.push_back(i);
}
printf("%I64d\n", ans);
return 0;
}