题意:
给定一个环形数组,求数组中连续长度不超过 K 的最大子段和。
思路:
1. 题目中给定的是环形数组,其实可以在数组尾部复制一份原数据,于是把问题规模扩大到了 2 * N,但是方便了解题。
2. 对数据在加以转换:sum[i] 表示 1~i 数组元素的和,由于题目中给定了数据范围,保证了 int 不会溢出。
3. 单调队列里面的数据表示 x~x+k 范围内 sum[] 的最小值。于是 sum[x+k+1] - sum[deq[s]] 即是以 x+k+1 为结尾的子序列最大和。
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
const int INFS = 0x7fffffff;
int sum[MAXN * 2], deq[MAXN * 2];
int main()
{
int cases;
scanf("%d", &cases);
while (cases--)
{
int N, K;
scanf("%d %d", &N, &K);
sum[0] = 0;
for (int i = 1; i <= N; ++i)
{
scanf("%d", &sum[i]);
sum[i+N] = sum[i];
}
for (int i = 1; i <= 2 * N; ++i)
sum[i] += sum[i-1];
int s = 0, e = -1;
int ans = -INFS, beg = 0, end = 0;
for (int i = 0; i < 2 * N; ++i)
{
if (i >= K && sum[i-K] == sum[deq[s]])
++s;
while (s <= e && sum[i] < sum[deq[e]])
--e;
deq[++e] = i;
if (sum[i+1] - sum[deq[s]] > ans)
{
ans = sum[i+1] - sum[deq[s]];
beg = deq[s] + 1, end = i + 1;
}
}
printf("%d %d %d\n", ans, (beg - 1) % N + 1, (end - 1) % N + 1);
}
return 0;
}