【题目】
题目描述:
输入一个长度为 n n n 的整数序列( a 1 a_1 a1, a 2 a_2 a2,……, a n a_n an),从中找出一段连续的长度不超过 m m m 的子序列,使得这个序列的和最大。
输入格式:
第一行两个数
n
n
n,
m
m
m
第二行由空格隔开的
n
n
n 个整数,即
a
1
a_1
a1 ~
a
n
a_n
an 。
输出格式:
输出一个整数,即最大连续子序列之和。
样例数据:
输入
4 2
-1 3 -10 20
输出
20
备注:
【数据范围】
30
%
30\%
30% 的数据:
n
n
n ≤
3000
3000
3000,
m
m
m ≤
100
100
100
100
%
100\%
100% 的数据
1
1
1 ≤
n
,
m
n,m
n,m ≤
1000000
1000000
1000000,
−
m
a
x
l
o
n
g
i
n
t
-maxlongint
−maxlongint ≤
a
i
a_i
ai ≤
m
a
x
l
o
n
g
i
n
t
maxlongint
maxlongint
【分析】
先吐槽一下这道题:
- 这道题数据略水啊,本来暴力 90 90 90 分,加个读优就 A 了。。。
- 调正解(单调队列版本)的时候忘开 l o n g    l o n g long\;long longlong 了,找了好久的错。。。
应该还是一个比较经典的模型吧,题解就是运用单调队列优化DP
统计一下前缀和, s u m i sum_i sumi 表示 1 + 2 + . . . + i 1+2+...+i 1+2+...+i 的值
以 a i a_i ai 结尾的最大子段和 f i = s u m i − m i n ( s u m i − k ) , k ≤ m f_i=sum_i-min(sum_{i-k}),k≤m fi=sumi−min(sumi−k),k≤m
由于我们想要 f i f_i fi 尽可能大,我们就要 s u m i − k sum_{i-k} sumi−k 要尽可能小,于是我们维护一个单调队列(单调递增),将更小的 s u m i sum_i sumi 放在前面,这样每次都直接取队首就行了
还是很简单吧
【代码】
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
#define inf 1ll<<31ll
using namespace std;
deque<int>q;
long long sum[N];
int main()
{
int n,m,i,x;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
scanf("%d",&x);
sum[i]=sum[i-1]+x;
}
q.push_back(0);
long long Max=-inf;
for(i=1;i<=n;++i)
{
while(!q.empty()&&i-q.front()>m) q.pop_front();
Max=max(Max,q.empty()?sum[i]:sum[i]-sum[q.front()]);
while(!q.empty()&&sum[i]<=sum[q.back()]) q.pop_back();
q.push_back(i);
}
printf("%lld",Max);
return 0;
}