Day10: GG高频面经 | LC1105.填充书架|动态规划| sundri

1105. 填充书架

难度中等149

给定一个数组 books ,其中 books[i] = [thicknessi, heighti] 表示第 i 本书的厚度和高度。你也会得到一个整数 shelfWidth

按顺序 将这些书摆放到总宽度为 shelfWidth 的书架上。

先选几本书放在书架上(它们的厚度之和小于等于书架的宽度 shelfWidth ),然后再建一层书架。重复这个过程,直到把所有的书都放在书架上。

需要注意的是,在上述过程的每个步骤中,摆放书的顺序与你整理好的顺序相同

  • 例如,如果这里有 5 本书,那么可能的一种摆放情况是:第一和第二本书放在第一层书架上,第三本书放在第二层书架上,第四和第五本书放在最后一层书架上。

每一层所摆放的书的最大高度就是这一层书架的层高,书架整体的高度为各层高之和。

以这种方式布置书架,返回书架整体可能的最小高度。

示例 1:

输入:books = [[1,1],[2,3],[2,3],[1,1],[1,1],[1,1],[1,2]], shelf_width = 4

输出:6

解释:

3 层书架的高度和为 1 + 3 + 2 = 6 。

第 2 本书不必放在第一层书架上。

示例 2:

输入: books = [[1,3],[2,4],[3,2]], shelfWidth = 6

输出: 4

提示:

  • 1 <= books.length <= 1000

  • 1 <= thicknessi <= shelfWidth <= 1000

  • 1 <= heighti <= 1000

#解题思路

注:真正帮我把这道题教会的是youtube上面的这个视频,浅显易懂,每一步都讲的非常清楚!感恩!

https://www.youtube.com/watch?v=nuG66q1E7dI

dp[0]:书架上没有书的时候,总高度就是0: dp[0] = 0

dp[1]:书架上只有一本书的时候,总高度就是这本书的高度;d[1] = books[0][1] = 1

dp[2]: 书架只有两本书的时候,因为book1 + book2的宽度并没有超过 shelfwidth,总高度是两本书放一起,取其中一本书的较大值。dp[2] = max(books[0][1], books[1][1]) = 3

dp[3]: 书架上有三本书的时候,分两种情况讨论:

case1: 第三本书另外开一个书架放,因此高度为 dp[2] + books[2][1] = 3+3 = 6

case2:

  1. 第三本书和第二本书放一起,第一本书自成一派:dp[1] + max(books[2][1], books[1][1]) = 1+3 = 4

  1. 第三本书和第二本书以及第一本书放一起,此时发现总宽度已经越界 cur_width > shelfwidth,不执行

......

之后的情况都是以此类推,大概的思路就是分成两种case:

  1. 第i本书自成一派,dp[i-1] + 当前书本的高度

  1. 当前这本书不断的尝试和前面的书放在一起,在宽度不超过shelfwidth的情况下,寻找最小的总高度

2.1第i本书和前1本书一起,两者高度先取最大值,总高度变成dp[i-2] + maxHeight

2.2第i本书和前2本书一起,三者高度先取最大值,总高度变成dp[i-3] + maxHeight

2.3 第i本书和前3本书一起,四者高度先取最大值,总高度变成dp[i-4] + maxHeight

2.4 ...... 以此类推,直到 cur_width 超过 shelfwidth,那么此时我们就跳出循环。

到此为止,case1和case2的情况都算出来了,最后就是取两者之间的较小值即可。

#解题代码

class Solution(object):
    def minHeightShelves(self, books, shelfWidth):
        """
        :type books: List[List[int]]
        :type shelfWidth: int
        :rtype: int
        """
        # case 1: put the ith book in the next shelf 
        # case 2: put the ith book together with other books without exceeding the width 
        # definition of dp[i]: the minimum height of the total ith book 
        # cur_width = the width of the shelf that put the ith book itself or with prev books
        # cur_height = the maximum height of the shelf that put the ith book itself or with pre books
        # total_height = the total height of all the ith books together

        dp = [0]
        for i in range(len(books)):
            cur_width = books[i][0] # 当前书架的宽度
            cur_height = books[i][1] # 当前书架的高度
            total_height = dp[i] + cur_height # 书架总体的最小高度 - case1
            j = i - 1
            while j >= 0 and cur_width + books[j][0] <= shelfWidth:
                cur_width += books[j][0] # 当前书架的宽度要加上上一本书的宽度
                cur_height = max(cur_height, books[j][1]) # 当前书架的高度要进行更新
                total_height = min(total_height, dp[j] + cur_height) # 总高度要进行更新 -- case2
                j -= 1 # 尝试再往前选一本书
            dp.append(min(total_height, dp[i] + books[i][1])) # 
        return dp[-1]

        # 犯错原因:在写case2情况的时候,没有把之前的 cur_height, cur_width, total_height的参数用上

#优秀代码参考

来自idontknoooo的解答:https://leetcode.com/idontknoooo/

class Solution:
    def minHeightShelves(self, books: List[List[int]], shelfWidth: int) -> int:
        n = len(books)
        dp = [sys.maxsize] * n
        dp[0] = books[0][1]                                # first book will always on it's own row
        for i in range(1, n):                              # for each book
            cur_w, height_max = books[i][0], books[i][1]
            dp[i] = dp[i-1] + height_max                   # initialize result for current book `dp[i]`
            for j in range(i-1, -1, -1):                   # for each previou `book[j]`, verify if it can be placed in the same row as `book[i]`
                if cur_w + books[j][0] > shelfWidth: break
                cur_w += books[j][0]
                height_max = max(height_max, books[j][1])  # update current max height
                dp[i] = min(dp[i], (dp[j-1] + height_max) if j-1 >= 0 else height_max) # always take the maximum heigh on current row
        return dp[n-1]

#时空复杂度

  • dp[i]: Minimum height sum for the first i books

  • Given the ith book, we just need to decide whether

  • It's gonna be on its own row, or

  • Append to some previous row

  • We can find out above with a nested loop O(N*N)

  • Time: O(N*N) 最坏情况下是书架非常的宽,case2要重复很多次,第二个循环无限接近N

  • Space: O(N) 使用了DP数组来存放结果

#解题心得:

这个题目大概花了我两周左右的时间,从懵懵懂懂,到真正完完全全理解,原来还是需要反反复复的磨练啊。非常感谢通过做这个题目了解到了一个专门讲动态规划的youtube频道。这个频道让我对这个题目第一次真正的理解了,之前看的什么dfs其实都只是照葫芦画瓢,只有自己真正理解,写出来的代码,才不是自欺欺人。虽然这个题目花了很多时间,但我觉得是真的非常值得!加油啊,慢慢的,一点一点的去进步!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值