周賽286。今天終於四題全過,夠我開心一整天。

題目

二維陣列piles代表錢幣堆,piles[i]從左到右代表錢幣由頂端到尾端的順序。
每堆錢幣一定要先拿頂端後,才能拿底下的。求拿k個錢幣最多可以獲得多少錢。

解法

k<=硬幣總數<=2000,size(piles)<=1000,一眼就知道DP。
剛開始思考方向錯誤,又差點歪到要記錄哪堆拿過幾個錢幣,好險及時拉回來。

定義dp(i,k)為到第i堆硬幣為止,總共拿了k個硬幣的最大值。答案就是回傳dp(N-1,k)。
在第i堆的時候,可以決定拿0~min(k,piles[i]大小)個硬幣,剩下的次數k留給i左邊的其他堆。
轉移方程式:dp(i,k)=max(sum(piles[i]前x項)+dp(i-1,k-x)) FOR ALL 0<=x<=size(piles[i])。
base cases:

  • i小於0,沒有這種錢堆了,回傳0
  • k等於0,沒辦法再拿了,回傳0
  • k<0,不允許多拿,回傳inf

雖然說轉移的時候有限制k的大小,理論上不會遇到k<0,以防萬一還是加了上去。

class Solution:
    def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int:
        N=len(piles)
        
        @lru_cache(None)
        def dp(i,k): #to ith pile,take k coins
            if i<0: #no such pile
                return 0
            if k==0: #no take
                return 0
            if k<0: #take too much
                return math.inf
            acc=0
            best=dp(i-1,k) #no take in this pile
            #try take 1~j coins in this pile
            for j in range(len(piles[i])):
                if j+1>k:
                    break
                acc+=piles[i][j]
                best=max(best,acc+dp(i-1,k-j-1))
            return best        
        
        return dp(N-1,k)