题目描述
输入一个长度为n的整数序列,从中找出一段不超过m的连续子序列,使得整个序列的和最大。
例如:1,-3,5,1,-2,3
当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6
输入
第一行两个数n,m
第二行有n个数,要求在n个数找到最大子序和
输出
一个数,数出他们的最大子序和
样例输入
6 4 1 -3 5 1 -2 3
样例输出
7
提示
30%满足 n,m<=100
50%满足 n,m<=3000
100%满足n,m<=300000
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int MX = 3e5;
typedef long long LL;
LL s[MX + 10];
int q[MX + 10];
//用队列来存(维护)每个点前面长度为m的段。
//i每往后移一格就往队列里插入一个数,
//同时弹出队头,保证队列里不能有多于m个数。
//队列里面有些数是没用的,
//需要求的是队列里面的最小值(被减数越小,值越大)。
//队列里面只要后面某个数比前面某个数小或相等,
//那么前面的数就可以删掉了。
int main()
{
int n, m;
scanf("%d%d", &n, &m);
int i;
for (i = 1; i <= n; i++)
{
scanf("%lld", &s[i]);
// 构造前缀和数组
s[i] += s[i - 1];
}
int hh = 0, tt = 0;
LL res = INT_MIN;
for (i = 1; i <= n; i++)
{
if (i - q[hh] > m)
// 范围长度在m以内
// 因为是一个一个往前挪的所以只需要if就可以
{
hh++;
}
res = max(res, s[i] - s[q[hh]]);
while (hh <= tt /*队列不空*/
&& s[q[tt]] >= s[i] /*队尾元素>=当前元素*/)
{
tt--;/*往下删,直到队尾比当前的数小*/
}
q[++tt] = i;//加进队列
}
// 上面的for循环是找最小值的过程,到for循环结束后,队列成为单调递增的。
// 这样队首元素s[q[hh]]就是最小前缀和
// 到最后取的是前缀和最小值(队首元素)记为s_min,
// s_min是最小的则s[i]-s_min是最大的,
// 所有s[i]-s_min的最大值就是最终的最大值,
// 即res.
printf("%lld\n", res);
return 0;
}
这道题终于弄明白了qwq