输入一个长度为 n 的整数序列,从中找出一段长度不超过 m 的连续子序列,使得子序列中所有数的和最大。
注意: 子序列的长度至少是 1。
输入格式
第一行输入两个整数 n,m。
第二行输入 n 个数,代表长度为 n 的整数序列。
同一行数之间用空格隔开。
输出格式
输出一个整数,代表该序列的最大子序和。
数据范围
1≤n,m≤300000
输入样例:
6 4
1 -3 5 1 -2 3
输出样例:
7
用单调队列优化,如果下标差距超过m,队首出队,利用前缀和去求一个区间的总和,队首即为当前下标范围的最小值,所以如果s[i]要<队尾队尾就要出队(while循环),每次用s[i]-队首的值(暴力枚举,s[i]每个都会被遍历,但是队首的那个是区间中最小的),即目前的最优值
点击查看代码
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll a[300010], s[300010];
deque<ll>q;
int main()
{
ios::sync_with_stdio(false);
int n, i, j, m;
cin >> n >> m;
ll ans = INT_MIN;
for (i = 1; i <= n; i++)
{
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
q.push_back(0);//初始化,s[i]-s[0]就是前i个的和
for (i = 1; i <= n; i++)
{
while (!q.empty() && i - q.front() > m)q.pop_front();
//队列中的数下标的差距不能大于m
ans = max(ans, s[i] - s[q.front()]);//更新ans,队首即当前下标范围中的最小值可以作为a[j]
while (!q.empty() && s[q.back()] >= s[i])q.pop_back();
//如果队尾的数比s[i]大那么队尾的数就不可能作为s[i]-s[j]中的s[j]
q.push_back(i);
}
cout << ans;
return 0;
}