题目链接:https://www.acwing.com/problem/content/137/
题意:输入一个长度为n的整数序列,从中找出一段长度不超过m的连续子序列,使得子序列中所有数的和最大。
注意: 子序列的长度至少是1。
数据范围
1≤n,m≤300000
输入样例:
6 4
1 -3 5 1 -2 3
输出样例:
7
思路:显然我们要在前缀和数组中sum[i]的前m范围内找到一个min,那么以第i个数结尾的m个连续元素的序列的最大子序和就是这个sum[i]-min;最后就只需要遍历一遍寻找答案啦。显然在寻找min的时候我们也可以循环枚举,但这样的时间复杂度就会很高啦;这个时候我们就可以优化一下,用一个队列存m范围内的数(不对弹出和加入来维护),而且如果队列中出现’大小’的两个数,那么前面那个大的数就永远不会用到(比小数先弹出,又不是min)所有整个队列就变成一个严格单调的队列了,而且队头就是我们寻找的min.
代码实现:
#include<iostream>
#include<limits.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
int n, m;
int q[N];
ll s[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++ ){
cin >> s[i];
s[i] += s[i - 1];
}
int hh = 0, tt = 0;
ll res = INT_MIN;
for(int i = 1; i <= n; i ++ ){
//如果队头超出m范围就将它弹出
if(i - q[hh] > m) hh ++ ;
res = max(res, s[i] - s[q[hh]]);
//先删除前面大的数再插入,保证队列单调
while(hh <= tt && s[q[tt]] >= s[i]) tt -- ;
q[ ++ tt] = i;
}
cout << res << endl;
return 0;
}