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:bash
Example 1:app
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:ui
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.
複製代碼
給出一個正整數數組 A,並給出兩個數值 L、M; 求這個數組中長度爲 L 和 M 的,且無重複元素的子數組的元素之和的最大值。spa
也就是求 V = (A[i] + A[i+1] + ... + A[i+L-1]) + (A[j] + A[j+1] + ... + A[j+M-1])
的最大值,其中翻譯
而且 i、j、L、M 知足:code
/** * @param {number[]} A * @param {number} L * @param {number} M * @return {number} */
var maxSumTwoNoOverlap = function(A, L, M) {
const prefixSum = [], length = A.length
prefixSum[0] = A[0]
for (let i=1; i<length; i++) {
prefixSum[i] = prefixSum[i-1] + A[i]
}
if (L + M === length){
return prefixSum[length - 1];
}
return Math.max(split_and_find_max_sum(A, prefixSum, L, M),
split_and_find_max_sum(A, prefixSum, M, L));
};
var split_and_find_max_sum = (A, prefixSum, L, M) => {
const length = A.length
const leftMax = new Array(length), rightMax = new Array(length);
for (let i = L-1; i<length; i++) {
const tmpSum = prefixSum[i] - prefixSum[i - L + 1] + A[i - L + 1];
if (i === L - 1) {
leftMax[i] = tmpSum;
} else {
leftMax[i] = Math.max(leftMax[i - 1], tmpSum);
}
}
for (let i = length - M; i >= 0; i--) {
const tmpSum = prefixSum[i + M - 1] - prefixSum[i] + A[i];
if (i === length - M) {
rightMax[i] = tmpSum;
} else {
rightMax[i] = Math.max(rightMax[i + 1], tmpSum);
}
}
let sum = -Infinity
for (let i = L - 1; i < length - M; i++) {
sum = Math.max(sum, leftMax[i] + rightMax[i + 1]);
}
return sum
}
複製代碼
這道題須要找到子數組元素和的最大值,那麼確定就須要求不少不少不少不一樣子數組的元素和,而後進行比對。orm
每次都將選出來的子數組元素逐一相加確定是不合算的。ip
這裏的方法是,創建一個 prefixSum 數組:
const prefixSum = [];
prefixSum[0] = A[0];
for (let i=1; i<length; i++) {
prefixSum[i] = prefixSum[i-1] + A[i]
}
複製代碼
這樣,對於數組 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
,若是咱們想求 2-7 這個子數組的和,只須要用 prefixSum[6] - prefixSum[0]
就好了。
這道題最容易想到的方法其實就是 —— 暴力搜索:
即,將全部長度爲 L 和 M 的子數組都找出來,兩兩配對,每一對都判斷一下它們是否是重合,若是重合就捨棄;若是沒有重合部分,就計算這兩個數組的和,而後找出最大值便可。
可是,效率真的低啊。。。雖然打着開心的 Accepted,可是。。。
Runtime:
100 ms, faster than 16.16% of JavaScript online submissions
for Maximum Sum of Two Non-Overlapping Subarrays.
複製代碼
不爽 =。=
因此該怎麼辦呢?
不用暴力搜索,那麼就要聰明的搜索。 這道題有個很關鍵的能夠「聰明搜索」的點,就是 non-overlapping
:既然必須是無重合的數組對,咱們一開始只須要尋找無重合的配對來求和就能夠了。
如何尋找?
...怎麼才能讓同桌的領地和本身的領地不要重合呢?...畫個三八線唄 =。=
同理,在數組中找一條**「分界線」**,在這條線兩邊分別尋找子數組,並讓每一個子數組的求和儘量大。 最後找到全部分界狀況下的子數組求和的最大值,就是答案了。
例如:有一個數組,[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
,選取數字 4,5 之間做爲分界線。那麼兩個子數組 C、D 分別在 [1, 2, 3, 4]
和 [5, 6, 7, 8, 9, 10]
中尋找,就必定能保證數組 C、D 不重合。同理,咱們還能夠把分界線選在 6,7 之間...
這樣,比暴力搜索就節省了不少次無用的比較。
函數 split_and_find_max_sum
其實就是在作畫分割線 & 尋找最大可能的和這件事情。
不過在函數中,它先分別從前日後(從後往前)計算了全部可能分界線的左邊(右邊)可能子元素的最大值。
這個是從前日後找:
for (let i = L-1; i<length; i++) {
const tmpSum = prefixSum[i] - prefixSum[i - L + 1] + A[i - L + 1];
if (i === L - 1) {
leftMax[i] = tmpSum;
} else {
leftMax[i] = Math.max(leftMax[i - 1], tmpSum);
}
}
複製代碼
這個 leftMax 就能夠認爲是,當分界線選了 i,i+1 之間的時候,分界線左邊全部長度爲 L 的子數組元素求和的最大值
這個是從後往前找:
for (let i = length - M; i >= 0; i--) {
const tmpSum = prefixSum[i + M - 1] - prefixSum[i] + A[i];
if (i === length - M) {
rightMax[i] = tmpSum;
} else {
rightMax[i] = Math.max(rightMax[i + 1], tmpSum);
}
}
複製代碼
rightMax 和 leftMax 基本同理。只不過 rightMax 選子數組的範圍是線右邊,同時數組長度是 M。
最後一輪 for 循環纔是在比對全部分界線狀況,並找出最大值。
let sum = -Infinity
for (let i = L - 1; i < length - M; i++) {
sum = Math.max(sum, leftMax[i] + rightMax[i + 1]);
}
return sum
複製代碼
函數 split_and_find_max_sum
須要調用兩次,緣由是兩個子數組長度可能不一樣,誰在線左邊,誰在線右邊,須要分狀況討論。