LeetCode 1186. 删除一次得到子数组最大和
题目描述


题意分析
先选择一个非空连续子数组,再从里面至多删除一个元素,使剩余元素的和最大。删除可以不用,但删除后也必须至少保留一个元素,不能通过清空一个负数区间得到零。
删除内部元素会让剩余元素在原数组中出现一个缺口,这是允许的;除这一次删除之外,不能再跳过其他位置。全负数组仍应返回某个负数,长度为一时只能保留唯一元素。
解法:单次删除状态动态规划
核心思路
[!blue]
沿数组从左到右处理,用两个状态区分删除机会是否已经使用。
keep表示以当前下标结尾、完全不删除的非空子数组最大和;它必须包含当前元素。deleted表示原区间已经处理到当前下标、恰好删除一次后的最大保留和,当前元素本身可以是被删除的那个位置。为保证始终非空,
deleted只记录在保留了至少一个元素后发生删除的方案,不单独记录删除原区间首项的表示。后者等价于直接把子数组起点右移,且还能保留删除机会,因此由keep或后续删除状态覆盖,不会损失最优答案。对于当前值
x,不删状态只有两种选择:接在旧的不删区间后面,或从当前值重新开始,所以新keep = max(旧keep + x, x)。已删状态同样分两类:此前已经删除过一次,则必须保留当前值,得到旧deleted + x;或者现在删除当前值,留下旧的不删区间,得到旧keep。取两者最大即可。计算删除当前这一分支时必须读取更新前的
keep。代码先用prevKeep保存它,再更新两个状态,避免拿包含当前元素的新状态表示“已经删除当前元素”。初始只看到第一个元素时,
keep和全局答案取首值,删除状态不可达,用安全的极小值表示;不能把它初始化为零,否则会允许空结果。之后每个下标都更新全局最大值,既考虑不删,也考虑已删,因为最优区间可能在任何位置结束。
解题步骤
- 令
keep = arr[0]、best = arr[0],用安全的极小值初始化不可达的deleted。- 从第二个元素开始,先把旧
keep保存到prevKeep。- 更新不删状态为
max(旧keep + arr[i], arr[i])。- 更新已删状态为
max(旧deleted + arr[i], prevKeep)。- 用本轮两个状态更新全局答案,扫描结束后返回
best。
代码实现
class Solution {
public int maximumSum(int[] arr) {
int keep = arr[0];
int deleted = Integer.MIN_VALUE / 2;
int best = arr[0];
for (int i = 1; i < arr.length; i++) {
// 删除当前元素时需要旧的未删除区间,先保留再更新。
int prevKeep = keep;
keep = Math.max(keep + arr[i], arr[i]);
// 要么延续已删除状态,要么删当前值并继承旧 keep。
deleted = Math.max(deleted + arr[i], prevKeep);
best = Math.max(best, Math.max(keep, deleted));
}
return best;
}
}
func maximumSum(arr []int) int {
keep := arr[0]
deleted := -1 << 30
best := arr[0]
for i := 1; i < len(arr); i++ {
// 删除当前元素时需要旧的未删除区间,先保留再更新。
prevKeep := keep
if keep+arr[i] > arr[i] {
keep = keep + arr[i]
} else {
keep = arr[i]
}
// 要么延续已删除状态,要么删当前值并继承旧 keep。
if deleted+arr[i] > prevKeep {
deleted = deleted + arr[i]
} else {
deleted = prevKeep
}
if keep > best {
best = keep
}
if deleted > best {
best = deleted
}
}
return best
}
复杂度分析
- 时间复杂度:
O(n)。每个元素只处理一次,每轮更新常数个状态。- 空间复杂度:
O(1)。状态只依赖上一轮,使用几个滚动变量即可。
关键点总结
[!green]
keep必须包含当前值,deleted的原区间右端到达当前值,但当前值可能已被删除。- 删除当前值继承旧不删区间,继续已删区间则必须加上当前值,两类不能混用。
- 全局答案同时考虑不删与已删,符合“至多一次”而不是“必须一次”。
- 首值初始化与不可达删除状态共同保证最终至少保留一个元素。
易错点总结
[!yellow]
- 把
best或初始已删状态设成零:会让全负输入错误地选择空结果。- 用更新后的
keep删除当前值:这个状态可能已经包含当前元素,必须保存上一轮的keep。- 已删状态不加当前值就继续传递:等价于再次跳过一个位置,超出一次删除限制。
- 只返回最后一个状态:最优子数组可能在更早位置结束,需要全局最大值。
- 只考虑恰好删除一次:不删除也可能最优,长度为一时更只能保留元素。
- 用整数最小值直接参与加法:不可达哨兵仍会加上当前值,应选择题目数值范围内不会溢出的安全极小值。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 53. 最大子数组和 | 中等 | 在Kadane状态上增加是否已删除一项的维度,子数组仍必须非空。 |
| 1493. 删掉一个元素以后全为 1 的最长子数组 | 中等 | 原题必须删除一项且元素为二进制,本题至多删除一次并最大化一般整数和。 |
| 152. 乘积最大子数组 | 中等 | 维护以当前位置结尾的最优连续区间;本题增加已经删除一次的状态,该题负数会交换最大与最小乘积状态。 |
| 918. 环形子数组的最大和 | 中等 | 维护以当前位置结尾的最优连续区间;本题增加已经删除一次的状态,该题同时计算最小区间和处理首尾相接。 |
| 1567. 乘积为正数的最长子数组长度 | 中等 | 维护以当前位置结尾的最优连续区间;本题增加已经删除一次的状态,该题只维护正负乘积对应的最长长度。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!