一.问题描述
Given an array A
of non-negative integers, return the maximum sum of elements in two non-overlapping (contiguous) subarrays, which have lengths L
and M
. (For clarification, the L
-length subarray could occur before or after the M
-length subarray.)
Formally, return the largest V
for which V = (A[i] + A[i+1] + ... + A[i+L-1]) + (A[j] + A[j+1] + ... + A[j+M-1])
and either:
0 <= i < i + L - 1 < j < j + M - 1 < A.length
, or0 <= j < j + M - 1 < i < i + L - 1 < A.length
.
Example 1:
Input: A = [0,6,5,2,2,5,1,9,4], L = 1, M = 2
Output: 20
Explanation: One choice of subarrays is [9] with length 1, and [6,5] with length 2.
Example 2:
Input: A = [3,8,1,3,2,1,8,9,0], L = 3, M = 2
Output: 29
Explanation: One choice of subarrays is [3,8,1] with length 3, and [8,9] with length 2.
Example 3:
Input: A = [2,1,5,6,0,9,5,0,3,8], L = 4, M = 3
Output: 31
Explanation: One choice of subarrays is [5,6,0,9] with length 4, and [3,8] with length 3.
Note:
L >= 1
M >= 1
L + M <= A.length <= 1000
0 <= A[i] <= 1000
二.解题思路
首先最简单的就是穷举,可以用回溯,不过感觉这题比较简单没必要用回溯
就是先计算从i到len(A)-L+1个数的之间所有可能的L个数想加的和
然后再计算之后可选的范围内的数的相加,之后两者相加
穷举最重要的就是剪枝操作,否则我觉得会超时,事实也确实如此
如果我们每次都是把从A[i]到A[i+L-1]之间的i个数想加,只能过到42个cases
我们注意到其实每次只要把之前求的sum减去A[i-1]然后加上A[i+L-1]之后的和与上面那行的算法结果是一样的
不过计算量大大减少
同时要注意边界
因为如果头尾两边不够M长度的话,是要跳过的,这时候剪枝要注意,i并不是连贯,所以剪枝所用的更新公式得放在continue语句前面,保证sumL是对的,当然你也可以用一个变量来跟踪
总结难点:
1.边界处理
2.剪枝操作
之后如果有更高效的方法我会更新
更多leetcode算法题解法请关注我的专栏leetcode算法从零到结束或关注我
欢迎大家一起套路一起刷题一起ac
更新:
在discuss里面看到一个O(n)的实现
核心思路是:首先对输入做一个处理,A[i]=A[i]+A[i-1],这样子A[j]-A[i]就是从i到j-1个数的和,不用再做加法
之后更新还是减去头加上尾,和我之前的差不多,不过for循环去掉不少更简洁
for循环去掉的方法是,首先定住一个sumM或者sumL最大值,然后迭代,每次框住L+M个元素,计算sumL和sumM,一边遍历一遍更新res,附java实现java实现,解释得很笼统,最好看源码
三.源码
class Solution:
def maxSumTwoNoOverlap(self, A: List[int], L: int, M: int) -> int:
if L<M:
tmp=L;L=M;M=tmp;
max_num=0
sumM=0
sumL=0;
for i in range(0,len(A)-L+1):
if i==0:
for t in range(i,i+L):
sumL+=A[t]
else:
# we should pay attention to this step
# prunning is important,otherwise you will meet TLS
# if you just simply add from A[i] to A[i+l-1], you will meet TLS
sumL=sumL-A[i-1]+A[i+L-1]
if i-1<M and i+L-1+M>len(A)-1:
continue
if i-1>=M:
sumM=0
for k in range(0,i-M+1):
if k==0:
for m in range(k,k+M):
sumM+=A[m]
else:
# this is the same as the prunning before
sumM=sumM-A[k-1]+A[k+M-1]
if sumM+sumL>max_num:
max_num=sumM+sumL
if i+L-1+M<=len(A)-1:
sumM=0
for k in range(i+L,len(A)-M+1):
if k==i+L:
for m in range(k,k+M):
sumM+=A[m]
else:
# this is the same as the prunning before
sumM=sumM-A[k-1]+A[k+M-1]
if sumM+sumL>max_num:
max_num=sumM+sumL
return max_num