最大子序和

最大子序和问题的单调队列解法:结合前缀和与单调队列优化。

一句话算法

长度受限的最大子段和,就是固定右端点时,在合法左端点里找最小前缀和。

问题模型

给定序列 a1,a2,,ana_1,a_2,\ldots,a_n 和长度限制 mm,求长度不超过 mm 的连续子段最大和。

设前缀和为:

Si=a1+a2++ai S_i = a_1+a_2+\cdots+a_i

区间 (j+1,i)(j+1,i) 的和为:

SiSj S_i-S_j

当右端点为 ii 时,左端点前缀下标 jj 必须满足:

imji1 i-m \le j \le i-1

所以要最大化 SiSjS_i-S_j,只需要在这个范围里找最小的 SjS_j

核心直觉

右端点 ii 从左到右移动时,合法的 jj 范围也是一个滑动窗口:

[i-m, i-1]

我们维护这个窗口里的最小前缀和下标,队头就是当前最优左端点。

算法步骤

  1. 预处理前缀和 S[0..n],其中 S[0]=0

  2. 用单调队列维护候选前缀下标 j

  3. 初始把 0 入队,表示子段可以从第 1 个元素开始。

  4. 从左到右枚举右端点 i

  5. 删除所有不满足 j >= i-m 的过期队头。

  6. 用队头更新答案:

    ans=max(ans,SiSq.front()) ans=\max(ans,S_i-S_{q.front()})
  7. 将当前前缀下标 i 加入队列前,弹出所有 S[q.back()] >= S_i 的下标,保持前缀和递增。

  8. i 加入队尾,继续处理下一个右端点。

算法证明

核心不变量: 处理右端点 ii 时,队列中保存的是所有仍合法、且可能成为最小前缀和的下标。

  1. 合法性。 子段长度不超过 mm,所以左端点前缀下标必须满足 jimj\ge i-m。每次先删除过期队头,队列中的下标都合法。
  2. 最优性。 对固定右端点 iiSiS_i 已确定。要最大化 SiSjS_i-S_j,只需要让 SjS_j 最小。
  3. 单调性。j<iS[j] >= S[i],那么 j 更早过期,前缀和还不更小,以后不可能比 i 更优,可以删除。
  4. 队头正确。 队列按前缀和值递增,队头就是当前合法范围内最小的前缀和下标。

因此每个右端点的最优子段都会被正确统计,所有右端点取最大值就是答案。

复杂度分析

每个前缀下标最多入队一次、出队一次。

  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(n)O(n),若只看队列规模为 O(m)O(m)

代码实现

cpp
        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h> using namespace std; int main() { int n, m; cin >> n >> m; vector<long long> prefix(n + 1, 0); for (int i = 1; i <= n; i++) { long long x; cin >> x; prefix[i] = prefix[i - 1] + x; } long long ans = LLONG_MIN; deque<int> q; q.push_back(0); for (int i = 1; i <= n; i++) { while (!q.empty() && q.front() < i - m) { q.pop_front(); } ans = max(ans, prefix[i] - prefix[q.front()]); while (!q.empty() && prefix[q.back()] >= prefix[i]) { q.pop_back(); } q.push_back(i); } cout << ans << "\n"; return 0; }

测试用例

输入:

6 3
1 -2 3 5 -1 2

输出:

8

解释:长度不超过 3 的最大子段是 3 5,和为 8

经典例题

1. AcWing 135

最大子序和模板题。重点是把“长度不超过 mm”变成前缀和下标窗口。

2. luogu-P1714

同一模型。枚举右端点,单调队列维护合法范围内的最小前缀和。

应用分类详解

长度受限最大子序和的本质是:固定右端点后,在一个滑动范围内找最小前缀和。

一、长度受限区间和

  • 典型模式: 连续子段长度不能超过或必须落在某个范围内,要求最大和或最小和。
  • 识别信号: 出现“长度不超过 mm”“连续子段”“最大区间和”。
  • 核心建模: 区间和写成 SiSjS_i-S_j,右端点固定时维护合法 jj 的最小前缀和。
应用场景 经典题目 核心思路
最大子序和 AcWing 135 队头是当前合法范围最小前缀和
长度限制最大连续和 luogu-P1714 枚举右端点,维护左端点窗口

二、前缀和差值优化

  • 典型模式: 目标可以写成当前值减去历史最优值。
  • 识别信号: 式子形如 current - min(previous),且 previous 的范围随当前位置单调移动。
  • 核心建模: 用单调队列维护历史候选中的最小值或最大值。
应用场景 经典题目 核心思路
区间收益最大化 常见收益题 当前收益减去窗口内最低成本
线性 DP 优化 决策窗口右移模型 队头保存当前最优前驱