鸡蛋掉落

问题:

给你 k 枚相同的鸡蛋,并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。

已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都会碎,从 f 楼层或比它低的楼层落下的鸡蛋都不会破。

每次操作,你可以取一枚没有碎的鸡蛋并把它从任一楼层 x 扔下(满足 1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。

请你计算并返回要确定 f 确切的值 的 最小操作次数 是多少?

本题是谷歌的一道经典面试题。由于本题过于经典,谷歌公司已经不再将这题作为面试的候选题目了。

解法:

题目是这样:你面前有一栋从 1 到 N 共 N 层的楼,然后给你 K 个鸡蛋(K 至少为 1)。现在确定这栋楼存在楼层 0 <= F <= N,在这层楼将鸡蛋扔下去,鸡蛋恰好没摔碎(高于 F 的楼层都会碎,低于 F 的楼层都不会碎)。现在问你,最坏情况下,你至少要扔几次鸡蛋,才能确定这个楼层 F 呢?

也就是让你找摔不碎鸡蛋的最高楼层 F,但什么叫「最坏情况」下「至少」要扔几次呢?我们分别举个例子就明白了。

比方说现在先不管鸡蛋个数的限制,有 7 层楼,你怎么去找鸡蛋恰好摔碎的那层楼?

最原始的方式就是线性扫描:我先在 1 楼扔一下,没碎,我再去 2 楼扔一下,没碎,我再去 3 楼……

以这种策略,最坏情况应该就是我试到第 7 层鸡蛋也没碎(F = 7),也就是我扔了 7 次鸡蛋。

先在你应该理解什么叫做「最坏情况」下了,鸡蛋破碎一定发生在搜索区间穷尽时,不会说你在第 1 层摔一下鸡蛋就碎了,这是你运气好,不是最坏情况。

现在再来理解一下什么叫「至少」要扔几次。依然不考虑鸡蛋个数限制,同样是 7 层楼,我们可以优化策略。

最好的策略是使用二分查找思路,我先去第 (1 + 7) / 2 = 4 层扔一下:

如果碎了说明 F 小于 4,我就去第 (1 + 3) / 2 = 2 层试……

如果没碎说明 F 大于等于 4,我就去第 (5 + 7) / 2 = 6 层试……

以这种策略,最坏情况应该是试到第 7 层鸡蛋还没碎(F = 7),或者鸡蛋一直碎到第 1 层(F = 0)。然而无论那种最坏情况,只需要试 log7 向上取整等于 3 次,比刚才尝试 7 次要少,这就是所谓的至少要扔几次。

Python:动态规划+备忘录+二分

class Solution:
    def superEggDrop(self, k: int, n: int) -> int:
        memo = {}
        def dp(K, N):
            if (K, N) not in memo:
                if K == 1: res = N 
                elif N == 0: res = 0
                # 二分缩小楼层范围
                else:
                    low, high = 1, N
                    while low + 1 < high:
                        mid = (high+low) // 2
                        s1 = dp(K-1, mid-1)
                        s2 = dp(K, N-mid)
                        if s2 > s1:
                            low = mid
                        elif s1 > s2:
                            high = mid
                        else:
                            low = high = mid
                    res = 1 + min(max(dp(K-1, i-1), dp(K, N-i)) for i in (low, high))
                memo[(K, N)] = res
            return memo[(K, N)]
        return dp(k, n)

 

posted @ 2022-08-27 19:57  今夜无风  阅读(60)  评论(0编辑  收藏  举报