3640. 三段式数组 II
考点
- 状态机DP
- 前后缀分解
思路
设数组为 \[ \text{nums} = [a_0,a_1,\dots,a_{n-1}] \] Trionic 子数组定义为一个连续子数组,可以被划分为三段连续区间:
- 第一段:严格递增
- 第二段:严格递减
- 第三段:严格递增
并且每一段至少包含 两个元素(即每段至少一条比较边)。
目标:求满足上述结构的子数组的 最大元素和。
一、前后缀分解解法
1. 核心思想
前后缀分解的思路是:
枚举中间的严格递减段,并在其左右分别拼接一个最优的严格递增段。
结构示意为: \[ \underbrace{\nearrow\ \nearrow}_{\text{左递增}} \quad \underbrace{\searrow\ \searrow}_{\text{中递减}} \quad \underbrace{\nearrow\ \nearrow}_{\text{右递增}} \] 只要三段都存在,且拼接后形成连续子数组,就可以计算其总和。
2. 状态定义
为此,算法设计了三类辅助数组(均为 1-based 索引):
(1) 前缀和数组 pre
\[ \text{pre}[i] = \sum_{k=0}^{i-1} a_k \]
用于在 \(O(1)\) 时间内计算任意连续区间的和。
初始化:
1 | pre[0] = 0; |
(2) incTail[i] —— 左递增段
\[ \text{incTail}[i] = \begin{cases} \text{以 } i \text{ 结尾的、严格递增连续子数组的最大和} \\ \text{长度至少为 2} \end{cases} \]
若不存在这样的递增段,则记为负无穷 neg。
转移逻辑(仅当递增成立时):
新开一个长度为 2 的递增段: \[ a_{i-2} + a_{i-1} \]
或在已有递增段后延长: \[ \text{incTail}[i-1] + a_{i-1} \]
因此: \[ \text{incTail}[i] = \max\left( a_{i-2}+a_{i-1},\ \text{incTail}[i-1]+a_{i-1} \right) \quad \text{若 } a_{i-2} < a_{i-1} \] 否则: \[ \text{incTail}[i] = -\infty \] 初始化:
1 | incTail[1] = neg; |
(3) incHead[i] —— 右递增段
\[ \text{incHead}[i] = \begin{cases} \text{以 } i \text{ 开头的、严格递增连续子数组的最大和} \\ \text{长度至少为 2} \end{cases} \]
该数组从右向左计算。
转移逻辑(当 \(a_{i-1} < a_i\) 时):
新开长度 2: \[ a_{i-1}+a_i \]
或向右延长: \[ a_{i-1} + \text{incHead}[i+1] \]
即: \[ \text{incHead}[i] = \max\left( a_{i-1}+a_i,\ \text{incHead}[i+1]+a_{i-1} \right) \] 初始化:
1 | incHead[n] = neg; |
3. 枚举中间递减段
算法使用双指针 (p,q) 表示一个连续严格递减区间:
p:递减段起点(1-based)q:递减段终点(1-based)
维护不变式:
- 当前区间
[p..q]是严格递减的 - 一旦递减性被破坏,重置
p = q
当满足: \[ a_{q-2} > a_{q-1} \] 时,可以尝试将三段拼接。
4. 答案计算公式
三段拼接后的总和为: \[
\begin{aligned}
\text{sum}
&=
\underbrace{\text{incTail}[p]}_{\text{左递增}}
+
\underbrace{(\text{pre}[q]-\text{pre}[p-1])}_{\text{中递减}}
+
\underbrace{\text{incHead}[q]}_{\text{右递增}} \\
&\quad
- a_{p-1} - a_{q-1}
\end{aligned}
\] 原因: incTail[p] 和 incHead[q]
都各自包含了端点元素,需要去重。
取所有合法情况中的最大值,即为最终答案。
5. AC代码
1 | class Solution { |
二、状态机 DP 解法
1. 核心思想
状态机 DP 的本质是:
在扫描数组时,始终维护“以当前位置为结尾”的、处于不同阶段的最优 Trionic 前缀子数组。
三段结构可以抽象为一个有限状态机: \[ \text{递增}_1 \;\longrightarrow\; \text{递减} \;\longrightarrow\; \text{递增}_2 \] 只需关心当前相邻两个元素的大小关系即可完成状态转移。
2. 状态定义
定义二维 DP 数组(1-based): \[ f[i][k] = \begin{cases} \text{以 } a_{i-1} \text{ 结尾,处于第 } k \text{ 段的最大和} \end{cases} \] 其中:
- \(k=0\):第一段严格递增
- \(k=1\):中间严格递减
- \(k=2\):第三段严格递增
所有状态均要求对应段 至少已形成一条合法比较边。
3. 初始值
在 \(i=1\)(只有一个元素)时,不可能形成任何合法段: \[ f[1][0] = f[1][1] = f[1][2] = -\infty \] 答案初始化为负无穷: \[ \text{res} = -\infty \]
4. 状态转移方程
设: \[ y = a_{i-2}, \quad x = a_{i-1} \]
(1) 第一段递增(f[i][0])
仅当 \(y < x\) 时合法: \[ f[i][0] = \max\bigl( y,\ f[i-1][0] \bigr) + x \] 含义:
y + x:新开一个长度为 2 的递增段f[i-1][0] + x:延续已有递增段
否则: \[ f[i][0] = -\infty \]
(2) 中间递减段(f[i][1])
仅当 \(y > x\) 时合法: \[ f[i][1] = \max\bigl( f[i-1][0],\ f[i-1][1] \bigr) + x \] 含义:
- 从第一段切换到递减段
- 或继续递减段
否则: \[ f[i][1] = -\infty \]
(3) 第三段递增(f[i][2])
仅当 \(y < x\) 时合法: \[ f[i][2] = \max\bigl( f[i-1][1],\ f[i-1][2] \bigr) + x \] 含义:
- 从递减段切换到第三段
- 或继续第三段
否则: \[ f[i][2] = -\infty \]
5. 答案更新
由于 f[i][2] 表示一个完整合法的 Trionic 子数组,故:
\[
\text{res} = \max(\text{res},\ f[i][2])
\]
6. AC代码
1 | class Solution { |
也可以滚动数组优化常数:
1 | class Solution { |