↓快轉到主要內容
  1. LeetCode/

LeetCode 2218: Maximum Value of K Coins From Piles

·1 分鐘· ·
LeetCode Hard Dynamic-Programming Knapsack Prefix-Sum Array
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2026-09-14

  • 提交紀錄:Accepted ×5(二維 → 一維,逐步優化)

解題思路
#

每一堆只能從上面拿,所以從一堆拿 x 個,一定是最上面的 x 個,價值是這堆的前綴和 prefix[x]。

每一堆就是一組,選項是「拿 0..len(pile) 個」,這是分組背包:

dp_i[j] = max over x of dp_{i-1}[j - x] + prefix_i[x]

dp[j] = 前 i 堆總共拿 j 個的最大價值。初始 dp[0] = 0、其他是負無限大(還沒辦法剛好拿 j 個)。

我的解法
#

class Solution:
    def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int:
        NEG = float("-inf")
        dp = [0] + [NEG] * k                       # dp[j]:目前為止總共拿 j 個
        for pile in piles:
            prefix = [0]
            for v in pile[:k]:                     # 一堆最多拿 k 個
                prefix.append(prefix[-1] + v)
            ndp = [NEG] * (k + 1)
            for j in range(k + 1):
                for x, val in enumerate(prefix):   # 這堆拿 x 個
                    if x > j:
                        break
                    ndp[j] = max(ndp[j], dp[j - x] + val)
            dp = ndp
        return dp[k]

我從二維 dp[i][j] 開始,逐步改成一維、每一堆再算前綴和;用新的 ndp 是為了避免同一堆被用兩次(也可以 j 倒著走、原地更新)。

複雜度
#

時間 O(k · Σ min(len(pile), k)),空間 O(k)。

要特別避免的錯誤
#

  • 以為可以從任意位置拿:只能拿最上面的,所以價值是前綴和。
  • 同一堆的選項互斥:要用新的陣列,或容量倒著走。

面試口說整理
#

每一堆只能從上面拿,拿 x 個的價值是這堆的前綴和,所以每一堆是一組、選項是拿幾個,這是分組背包。dp[j] 是目前為止總共拿 j 個的最大價值,每一堆枚舉拿幾個更新到新的陣列。時間 O(k · 總硬幣數)。

相關文章

LeetCode 4041: Minimum Operations to Form Subset Sum II
·2 分鐘
LeetCode Hard Dynamic-Programming Knapsack Breadth-First-Search Array
LeetCode 4041 解題筆記:乘除可以任意順序,用 BFS 找每個數能變成的所有值(不超過 sum)和最少步數,再做跟 4040 一樣的分組 0/1 背包
LeetCode 2585: Number of Ways to Earn Points
·1 分鐘
LeetCode Hard Dynamic-Programming Knapsack Array
LeetCode 2585 解題筆記:有數量上限的背包(分組背包);每種題目選 0 到 count 題,容量倒著走,在裡面枚舉選幾題
LeetCode 410: Split Array Largest Sum
·1 分鐘
LeetCode Hard Dynamic-Programming Binary-Search Greedy Prefix-Sum Array
LeetCode 410 解題筆記:對答案二分搜尋;給定上限 mid,貪心地能塞就塞,數出最少要切幾段,段數 <= k 就表示 mid 可行
LeetCode 1246: Palindrome Removal
·1 分鐘
LeetCode Hard Dynamic-Programming Interval-Dp Array
LeetCode 1246 解題筆記:區間 DP;arr[i] 自己刪、兩端相同時跟裡面一起刪,或切成兩段,三種取最小
LeetCode 2163: Minimum Difference in Sums After Removal of Elements
·2 分鐘
LeetCode Hard Dynamic-Programming Heap Array
LeetCode 2163 解題筆記:枚舉第一段和第二段的分界 k;左邊 nums[:k] 選 n 個最小的(最大堆維護),右邊 nums[k:] 選 n 個最大的(最小堆維護),前後綴各掃一次
LeetCode 4040: Minimum Operations to Form Subset Sum I
·2 分鐘
LeetCode Medium Dynamic-Programming Knapsack Array
LeetCode 4040 解題筆記:先乘後除時,最佳的做法只會是「只乘」或「只除」;列出每個數能變成的值和步數,再做分組 0/1 背包