题目地址:
https://www.acwing.com/problem/content/description/137/
输入一个长度为 n n n的整数序列,从中找出一段长度不超过 m m m的连续子序列,使得子序列中所有数的和最大。注意:子序列的长度至少是 1 1 1。
输入格式:
第一行输入两个整数
n
,
m
n,m
n,m。
第二行输入
n
n
n个数,代表长度为
n
n
n的整数序列。同一行数之间用空格隔开。
输出格式:
输出一个整数,代表该序列的最大子序和。
数据范围:
1
≤
n
,
m
≤
300000
1≤n,m≤300000
1≤n,m≤300000
考虑其前缀和数组 p p p,枚举 p [ i ] p[i] p[i],那么就是要找到 min i − j ≤ m { p [ j ] } \min_{i-j\le m}\{p[j]\} mini−j≤m{p[j]},这样 p [ i ] − min i − j ≤ m { p [ j ] } p[i] - \min_{i-j\le m}\{p[j]\} p[i]−mini−j≤m{p[j]}就是以序列第 i i i个数为结尾的长度不超过 m m m的和最大子数组的和,这其实就是求滑动窗口最小值的问题,可以用单调队列。代码如下:
#include <iostream>
#include <deque>
#define x first
#define y second
using namespace std;
const int INF = 1e9;
int n, m;
deque<pair<int, int>> dq;
int main() {
scanf("%d%d", &n, &m);
int res = -INF;
dq.push_back({0, 0});
for (int i = 1, s = 0; i <= n; i++) {
int a;
scanf("%d", &a);
s += a;
if (dq.size() && i - dq.front().x > m) dq.pop_front();
res = max(res, s - dq.front().y);
while (dq.size() && dq.back().y >= s) dq.pop_back();
dq.push_back({i, s});
}
printf("%d\n", res);
}
时间复杂度 O ( n ) O(n) O(n),空间 O ( m ) O(m) O(m)。