【Lintcode】1850. Pick Apples

記錄演算法發表於2020-12-15

題目地址:

https://www.lintcode.com/problem/pick-apples/description

給定一個長 n n n的陣列 A A A,再給定兩個正整數 L L L K K K,要求在 A A A中找到兩個不相交的長度各自為 L L L K K K的子區間,使得這兩個子區間的所有數之和最大。返回這個最大和。

思路是字首和。先求出 A A A的字首和陣列 p p p,使得 p [ i ] = ∑ A [ 0 : i − 1 ] p[i]=\sum A[0:i-1] p[i]=A[0:i1],再求字尾和陣列 s s s,使得 s [ i ] = ∑ A [ i : n − 1 ] s[i]=\sum A[i:n-1] s[i]=A[i:n1]。接下來按照這兩個區間誰在左誰在右來分類,無論怎樣,我們可以列舉左區間的右端點,這時左區間的和可以用 p p p陣列快速算出來,然後這個和再加上右區間可能取值的最大值,當列舉完左區間之後,就得到了左右區間之和的最大值了。例如,我們先考慮左 L L L K K K,那麼左區間的右端點的範圍就是 [ L − 1 : n − K − 1 ] [L-1:n-K-1] [L1:nK1],再算 A [ n − K : n − 1 ] A[n-K:n-1] A[nK:n1]的最大的長為 K K K的區間的和即可。為了快速算出 A [ n − K : n − 1 ] A[n-K:n-1] A[nK:n1]的最大的長為 K K K的區間的和,我們從右向左遍歷 [ L − 1 : n − K − 1 ] [L-1:n-K-1] [L1:nK1],然後用一個變數存右邊剩餘部分最大的長為 K K K的區間的和。即要求: max ⁡ i : n − K → L { p [ i ] − p [ i − L ] + max ⁡ j : n − K → i { s [ j ] − s [ j + K ] } } \max_{i:n-K\to L} \{p[i]-p[i-L]+\max_{j:n-K\to i}\{s[j]-s[j+K]\}\} i:nKLmax{p[i]p[iL]+j:nKimax{s[j]s[j+K]}}上述技巧可以讓 max ⁡ j : n − K → i { s [ j ] − s [ j + K ] } \max_{j:n-K\to i}\{s[j]-s[j+K]\} maxj:nKi{s[j]s[j+K]} O ( 1 ) O(1) O(1)時間內算出。程式碼如下:

public class Solution {
    /**
     * @param A: a list of integer
     * @param K: a integer
     * @param L: a integer
     * @return: return the maximum number of apples that they can collect.
     */
    public int PickApples(int[] A, int K, int L) {
        // write your code here
        // 算字首和和字尾和
        int[] preSum = new int[A.length + 1], sufSum = new int[A.length + 1];
        for (int i = 0; i < A.length; i++) {
            preSum[i + 1] = preSum[i] + A[i];
        }
        for (int i = A.length; i > 0; i--) {
            sufSum[i - 1] = sufSum[i] + A[i - 1];
        }
        
        int res = -1;
        
        // 先算左L右K
        res = Math.max(res, calc(A, L, K, preSum, sufSum));
        // 再算左K右L
        res = Math.max(res, calc(A, K, L, preSum, sufSum));
        
        return res;
    }
    
    // 算左區間長l右區間長r的情況下,最大的兩區間的總和
    private int calc(int[] A, int l, int r, int[] preSum, int[] sufSum) {
        int res = -1;
        // i是列舉左區間的右端點的右邊一位,maxR存的是左區間右邊的可選長r的區間的最大和
        for (int i = A.length - r, maxR = 0; i >= l; i--) {
            maxR = Math.max(maxR, sufSum[i] - sufSum[i + r]);
            res = Math.max(res, maxR + preSum[i] - preSum[i - l]);
        }
        
        return res;
    }
}

時空複雜度 O ( n ) O(n) O(n)

相關文章