最大子序和
输入一个长度为n的整数序列,从中找出一段长度不超过m的连续子序列,使得子序列中所有数的和最大。
注意: 子序列的长度至少是1。
输入格式
第一行输入两个整数n,m。
第二行输入n个数,代表长度为n的整数序列。
同一行数之间用空格隔开。
输出格式
输出一个整数,代表该序列的最大子序和。
数据范围
1≤n,m≤300000
输入样例:
6 4
1 -3 5 1 -2 3
输出样例:
7
求取自序和(或子串和)我们首先想到的是利用前缀和数组求取,当我们利用前缀和数组可以求取最大的一个自序和,但不能保证该最大自序和的长度;因此我们此时需要动态维护一个最大为m的数组来存储当前位置前m个的前缀和,并且我们还要能很快的找到其中的最小值;
这就引出了我们今天要用到的知识点“单调队列”,我们每次移动一位时,通过维护单调队列,使其满足其中所有的元素到达当前位置不超过m(我们只需要保持队首满足就行),另外我们要寻找当前能找到的最小值,即要维护一个单调递增队列,这样我们维护队列的时间复杂度是O(n)的查找时间复杂度是O(1)的;
#include<iostream>
#include<algorithm>
#include<limits.h>
using namespace std;
const int N=300010;
int n,m;
int s[N],q[N];//s数组用于存储前缀和
//q数组用于动态维护最多m长度的单调队列
//q数组存储的是s数组中的位置
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
{ //建立前缀和数组
cin>>s[i];
s[i]+=s[i-1];
}
int res=INT_MIN;//求取数值的最大值,初始值设置为最小值
int tt=0,hh=0;//tt为动态数组的队尾,hh为动态数组的队头
//该动态数组存储的位置可以对应到s数组中形成单调递增数组
//这样队首才是当前能找到的最小值
for(int i=1;i<=n;i++)
{
if(q[hh]<i-m)hh++;//该动态数组中的队首存储的位置与i的距离
//相差超过m时,此时要维护该动态数组的大小
res=max(res,s[i]-s[q[hh]]);
//正因为维护的是单调递增队列因此当前所能找到的最大
//子串和为s[i]-s[q[hh]]
while(hh<=tt&&s[i]<s[q[tt]])tt--;
//维护使该动态数组保持单调递增性
q[++tt]=i;//动态数组将当前位置入队
}
cout<<res<<endl;
return 0;
}