[心得] Maximum sum k-disjoint subarrays

看板Prob_Solve (計算數學 Problem Solving)作者 (喔喔)時間8年前 (2016/03/04 09:22), 8年前編輯推噓3(304)
留言7則, 3人參與, 最新討論串1/2 (看更多)
題目:給定一含有 n 個整數的陣列 A ,找出不相交的 k 個子陣列其總和最大。 也就是要找 k 組索引對 (b1, e1), ... (bk, ek), bi < ei 且 ei < b_{i+1} 使得 A[bi...ei] 的總和最大。 出處: http://www.lintcode.com/en/problem/maximum-subarray-iii/ 這問題理論上線性時間可解,可以參考 http://goo.gl/llwDs8http://arxiv.org/pdf/1410.2847.pdf 但是方法有點複雜。 首先,我們可以把問題稍微簡化一點,把所有為零的元素去掉,頭尾的 負數去掉,然後把所有同號的子陣列合併為一個元素。 在不失一般性的情況下可以假設陣列中有多於 k 個正整數 所以我們可以假設下列性質 所有偶數的 i, A[i] 為正 所有奇數的 i, A[i] 為負 (陣列是從 0 起算) A[0] 和 A[n-1] 皆為正 且 n > 2k - 1 。 因為是要算子陣列的總和,可以使用 prefix sum 的技巧。 令 P[i] = A[0..i] 的總和,那麼 A[i..j] 的總和就可以用 P[j] - P[i - 1] 來求(假設 P[-1] = 0),又因為 A 陣列是正負交替出現的, P 陣列會滿足 對於所有偶數的 i, P[i-1] < P[i] > P[i+1] 如果 i-1, i+1 合法 所以這問題可以轉換成以下問題 給定一含有 n 個整數的陣列 P , P[i-1] < P[i] > P[i+1] 找出 k 組索引對 (b1, e1), ... (bk, ek), bi < ei 且 ei < b_{i+1} 使得 P[ei] - P[bi] 總和最大 (P[-1] = 0) 可以把 P[i] 當成股票在第 i 天的價格,允許作 k 次交易,求最大的獲利。 出處: http://codeforces.com/problemset/problem/391/F3 https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/ 同樣這題也是線性時間可解的 http://www.tachenov.name/2016/best-time-to-buy-and-sell-stock-iv/ https://maskray.me/blog/2015-03-27-leetcode-best-time-to-buy-and-sell-stock-iv 我覺得這個解法有點難懂,所以推敲了很久,以下是我思考的結果: 考慮兩組不相交索引對 (b, e) 和 (b', e'), e < b' 其在 P 中相對應的值為 [l, h] 和 [l', h'] 亦即 l = P[b], h = P[e], l' = P[b'] 和 h' = P[e'] (l代表低點, h 代表高點) 只考慮 l < h 和 l' < h' 的情況,因為買高賣低沒意義。 如果 l >= l' ,那麼我們不需要考慮 b 跟 e' 之後的索引配對的情況,因 為可以用 l' 來配對,所以我們可以忽略所有(b, e) 對 (b', e')之後的影響。 如果 l < l' ,那 l 就有可能會跟 e' 之後的索引配對,所以(b, e)要保留。 我們可以一個一個考慮索引對 (b, b+1) for b = -1, 1, ... n - 2 然後用一個 stack 來維護可跟之後元素結合的索引對。 此 stack 會滿足一個單調性: 令 (b1, e1), ..., (bt, et) 為 stack 中的索引對(t為頂端),這 些索引對必不相交,且 ei < b_{i+1}。 令 [l1, h1], ..., [lt, ht] 為索引在 P 中相對應的值,則必滿足 [li, hi] 嚴格包含 [l_{i+1}, h_{i+1}] 如果把 [l, h] 想成區間 而維護這個性質也不難,當新進來一組索引對 (b, e),其起點 b 必在所 有在 stack 中的索引對之後。令其對應的值為 [l = P[b], h = P[e]] 如果 lt >= l, 那麼 stack 頂端的索引對以後不會需要了,把此索引對 從 stack 中移除,把 [lt, ht] 丟到一個 list L 裡面,以備最後挑選。 重複此動作直到 lt < l 為止。 當 lt < l 時,有兩種情況: ht > h: 亦即 [l, h] 是被 [lt, ht] 包含的,那就把 [l, h] 放到 stack 裡面。 ht <= h: 這情況比較麻煩,因為 [lt, ht] 和 [l, h] 沒有包含關係。 此時我們可以假裝把 [lt, ht] 和 [l, h] 合併為 [lt, h]。 因為 lt < l ,所有可以跟 l 配對的都不會比跟 lt 配對 來的好,所以並不會漏掉可以配對的情況。 但是這樣作會漏掉把 [lt, ht] 和 [l, h] 分別選取的情況。 不過我們知道分別選擇會得到 (ht - lt) + (h - l) = (h - lt) + (ht - l) 也就是說分別選擇的話,可多得到 ht - l 。 所以如果 ht - l > 0 的話,我們可以把一個假的區間 [l, ht] 丟到 L 中,其值為(ht - l),以備最後挑選。 重複此動作直到 ht > h。 當把所有索引對處理完之後,把 stack 中所有的索引對其相對應的值區間 都丟到 L 中,從 L 裡面挑出值最大的 k 個就是答案了。 感覺還是有點複雜,不知道有沒有比較簡單的想法。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 65.96.6.117 ※ 文章網址: https://www.ptt.cc/bbs/Prob_Solve/M.1457054558.A.D5F.html

03/04 14:55, , 1F
是最多找k個還是一定要找k個,如果一定要找k個,那就算賺
03/04 14:55, 1F

03/04 14:56, , 2F
的數量不足k個,賠錢還是要買滿k次
03/04 14:56, 2F

03/04 19:29, , 3F
因為我原本問題的假設是至少有 k 個正整數 所以轉化過後
03/04 19:29, 3F

03/04 19:30, , 4F
就一定可以找到 k 個.. 如果是直接考慮股票買賣問題的話
03/04 19:30, 4F

03/04 19:30, , 5F
應該是最多可以找 k 個
03/04 19:30, 5F

03/04 20:26, , 6F
不過不到k個正數也沒差就是了,取前k大的加起來就是答案
03/04 20:26, 6F

03/04 21:10, , 7F
03/04 21:10, 7F
※ 編輯: FRAXIS (65.96.6.117), 03/05/2016 06:34:21
文章代碼(AID): #1MsEDUrV (Prob_Solve)
文章代碼(AID): #1MsEDUrV (Prob_Solve)