【LeetCode题解】1186.删除一次得到子数组最大和

题目地址

https://leetcode-cn.com/problems/maximum-subarray-sum-with-one-deletion/

题目描述


   
   
  1. 给你一个整数数组,返回它的某个 非空 子数组(连续元素)在执行一次可选的删除操作后,所能得到的最大元素总和。

  2. 换句话说,你可以从原数组中选出一个子数组,并可以决定要不要从中删除一个元素(只能删一次哦),(删除后)子数组中至少应当有一个元素,然后该子数组(剩下)的元素总和是所有子数组之中最大的。

  3. 注意,删除一个元素后,子数组 不能为空。

  4. 请看示例:

  5. 示例 1:

  6. 输入:arr = [1,-2,0,3]

  7. 输出:4

  8. 解释:我们可以选出 [1, -2, 0, 3],然后删掉 -2,这样得到 [1, 0, 3],和最大。

  9. 示例 2:

  10. 输入:arr = [1,-2,-2,3]

  11. 输出:3

  12. 解释:我们直接选出 [3],这就是最大和。

  13. 示例 3:

  14. 输入:arr = [-1,-1,-1,-1]

  15. 输出:-1

  16. 解释:最后得到的子数组不能为空,所以我们不能选择 [-1] 并从中删去 -1 来得到 0。

  17. 我们应该直接选择 [-1],或者选择 [-1, -1] 再从中删去一个 -1。

  18.  

  19. 提示:

  20. 1 <= arr.length <= 10^5

  21. -10^4 <= arr[i] <= 10^4

思路

暴力法

符合知觉的做法是求出所有的情况,然后取出最大的。我们只需要两层循环接口,外循环用于确定我们丢弃的元素,内循环用于计算subArraySum。


   
   
  1. class Solution:

  2. def maximumSum(self, arr: List[int]) -> int:

  3. res = arr[0]

  4. def maxSubSum(arr, skip):

  5. res = maxSub = float("-inf")

  6. for i in range(len(arr)):

  7. if i == skip:

  8. continue

  9. maxSub = max(arr[i], maxSub + arr[i])

  10. res = max(res, maxSub)

  11. return res

  12. # 这里循环到了len(arr)项,表示的是一个都不删除的情况

  13. for i in range(len(arr) + 1):

  14. res = max(res, maxSubSum(arr, i))

  15. 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]的子数组最大值

  • 上面的上个步骤得到了删除一个的子数组最大值, 不删除的只需要在上面循环顺便计算一下即可


   
   
  1. class Solution:

  2. def maximumSum(self, arr: List[int]) -> int:

  3. n = len(arr)

  4. l = [arr[0]] * n

  5. r = [arr[n - 1]] * n

  6. if n == 1:

  7. return arr[0]

  8. res = arr[0]

  9. for i in range(1, n):

  10. l[i] = max(l[i - 1] + arr[i], arr[i])

  11. res = max(res, l[i])

  12. for i in range(n - 2, -1, -1):

  13. r[i] = max(r[i + 1] + arr[i], arr[i])

  14. res = max(res, r[i])

  15. for i in range(1, n - 1):

  16. res = max(res, l[i - 1] + r[i + 1])

  17. 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]), 一个都不删除


   
   
  1. #

  2. # @lc app=leetcode.cn id=1186 lang=python3

  3. #

  4. # [1186] 删除一次得到子数组最大和

  5. #

  6. # @lc code=start

  7. class Solution:

  8. def maximumSum(self, arr: List[int]) -> int:

  9. # DP

  10. max0 = arr[0]

  11. max1 = arr[0]

  12. res = arr[0]

  13. n = len(arr)

  14. if n == 1:

  15. return max0

  16. for i in range(1, n):

  17. # 先更新max1,再更新max0,因为max1用到了上一个max0

  18. max1 = max(max1 + arr[i], max0)

  19. max0 = max(max0 + arr[i], arr[i])

  20. res = max(res, max0, max1)

  21. return res

  22. # @lc code=end

关键点解析

  • 空间换时间

  • 头尾双数组

  • 动态规划

相关题目

  • 42.trapping-rain-water

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值