leecode/problems/1186.maximum-subarray-sum-with-one-deletion.md
2020-05-22 18:17:19 +08:00

162 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 题目地址
https://leetcode.com/problems/maximum-subarray-sum-with-one-deletion/
## 题目描述
```
给你一个整数数组,返回它的某个 非空 子数组(连续元素)在执行一次可选的删除操作后,所能得到的最大元素总和。
换句话说,你可以从原数组中选出一个子数组,并可以决定要不要从中删除一个元素(只能删一次哦),(删除后)子数组中至少应当有一个元素,然后该子数组(剩下)的元素总和是所有子数组之中最大的。
注意,删除一个元素后,子数组 不能为空。
请看示例:
示例 1
输入arr = [1,-2,0,3]
输出4
解释:我们可以选出 [1, -2, 0, 3],然后删掉 -2这样得到 [1, 0, 3],和最大。
示例 2
输入arr = [1,-2,-2,3]
输出3
解释:我们直接选出 [3],这就是最大和。
示例 3
输入arr = [-1,-1,-1,-1]
输出:-1
解释:最后得到的子数组不能为空,所以我们不能选择 [-1] 并从中删去 -1 来得到 0。
我们应该直接选择 [-1],或者选择 [-1, -1] 再从中删去一个 -1。
 
提示:
1 <= arr.length <= 10^5
-10^4 <= arr[i] <= 10^4
```
## 思路
### 暴力法
符合知觉的做法是求出所有的情况,然后取出最大的。 我们只需要两层循环接口外循环用于确定我们丢弃的元素内循环用于计算subArraySum。
```python
class Solution:
def maximumSum(self, arr: List[int]) -> int:
res = arr[0]
def maxSubSum(arr, skip):
res = maxSub = float("-inf")
for i in range(len(arr)):
if i == skip:
continue
maxSub = max(arr[i], maxSub + arr[i])
res = max(res, maxSub)
return res
# 这里循环到了len(arr)项,表示的是一个都不删除的情况
for i in range(len(arr) + 1):
res = max(res, maxSubSum(arr, i))
return res
```
### 空间换时间
上面的做法在LC上会TLE 因此我们需要换一种思路既然超时了我们是否可以从空间换时间的角度思考呢我们可以分别从头尾遍历建立两个subArraySub的数组l和r。 其实这个不难想到,很多题目都用到了这个技巧。
具体做法:
- 一层遍历, 建立l数组l[i]表示从左边开始的以arr[i]结尾的subArraySum的最大值
- 一层遍历, 建立r数组r[i]表示从右边开始的以arr[i]结尾的subArraySum的最大值
- 一层遍历, 计算 l[i - 1] + r[i + 1] 的最大值
> l[i - 1] + r[i + 1]的含义就是删除arr[i]的子数组最大值
- 上面的这个步骤得到了删除一个的子数组最大值, 不删除的只需要在上面循环顺便计算一下即可
```python
class Solution:
def maximumSum(self, arr: List[int]) -> int:
n = len(arr)
l = [arr[0]] * n
r = [arr[n - 1]] * n
if n == 1:
return arr[0]
res = arr[0]
for i in range(1, n):
l[i] = max(l[i - 1] + arr[i], arr[i])
res = max(res, l[i])
for i in range(n - 2, -1, -1):
r[i] = max(r[i + 1] + arr[i], arr[i])
res = max(res, r[i])
for i in range(1, n - 1):
res = max(res, l[i - 1] + r[i + 1])
return res
```
### 动态规划
上面的算法虽然时间上有所改善但是正如标题所说空间复杂度是O(n),有没有办法改进呢?答案是使用动态规划。
具体过程:
- 定义max0表示以arr[i]结尾且一个都不漏的最大子数组和
- 定义max1表示以arr[i]或者arr[i - 1]结尾,可以漏一个的最大子数组和
- 遍历数组更新max1和max0注意先更新max1因为max1用到了上一个max0
- 其中` max1 = max(max1 + arr[i], max0)`, 即删除arr[i - 1]或者删除arr[i]
- 其中` max0 = max(max0 + arr[i], arr[i])` 一个都不删除
```python
#
# @lc app=leetcode.cn id=1186 lang=python3
#
# [1186] 删除一次得到子数组最大和
#
# @lc code=start
class Solution:
def maximumSum(self, arr: List[int]) -> int:
# DP
max0 = arr[0]
max1 = arr[0]
res = arr[0]
n = len(arr)
if n == 1:
return max0
for i in range(1, n):
# 先更新max1再更新max0因为max1用到了上一个max0
max1 = max(max1 + arr[i], max0)
max0 = max(max0 + arr[i], arr[i])
res = max(res, max0, max1)
return res
# @lc code=end
```
## 关键点解析
- 空间换时间
- 头尾双数组
- 动态规划
## 相关题目
- [42.trapping-rain-water](./42.trapping-rain-water.md)