好一陣子沒DP,找一題來玩玩。

題目

books陣列代表每本書的寬度及高度,而shelfWidth代表書櫃的寬度。你必須依照books的出現順序將所有書塞入書櫃。
你可以選擇在任何時候加上一片隔板,建立新的平台在下方書籍的最高點上,繼續塞入剩下的書籍。求塞完所有書後,所堆積到的最小高度為多少。

解法

例題展示了有些時候塞滿每層並不是最佳解法,適時的換層也許會更好,那一定是DP了。
定義dp(i)為塞完第i本書的最低高度。
轉移方程式:dp(i)=min(這層n本書的最高高度+塞完第i-n本書的最低高度),n取決於每本書的寬度總和不超過shelfWidth。
i<0為base case,因為不存在第-1本書,所以回傳高度0。
回傳dp[N-1]就是答案。

class Solution:
    def minHeightShelves(self, books: List[List[int]], shelfWidth: int) -> int:
        
        @lru_cache(None)
        def dp(i):
            if i<0:
                return 0
            best=math.inf
            h=0
            j=i
            w=shelfWidth
            while j>=0 and w>=books[j][0]:
                w-=books[j][0]
                h=max(h,books[j][1])   
                j-=1
                best=min(best,h+dp(j))
            return best
        
        return dp(len(books)-1)

改成bottom up版本。

class Solution:
    def minHeightShelves(self, books: List[List[int]], shelfWidth: int) -> int:
        N=len(books)
        dp=[math.inf]*N
        
        for i in range(N):
            j=i
            w=h=0
            while j>=0 and w+books[j][0]<=shelfWidth:
                w+=books[j][0]
                h=max(h,books[j][1])
                prev=dp[j-1] if j>0 else 0
                dp[i]=min(dp[i],prev+h)
                j-=1
        
        return dp[-1]