leetcode扔鸡蛋问题总结

扔鸡蛋问题是一道经典的面试题,他的简单一点的问法是给两个鸡蛋,一百层楼,找到最少的尝试次数,找到鸡蛋不会摔碎的临界楼层。

这个问题的解法有这么几种,二分法,平方根法,解方程法,具体的解释可以看一下这个博客,这里详细说一下最优解法解方程法,

假设问题存在最优解(扔鸡蛋过程),这个解的最坏情况尝试次数是x次,那么,我们第一次扔鸡蛋该选择哪一层?

恰恰是从第x层开始扔,选择更高一层或是更低一层都不合适

为什么第一次扔就要选择第x层呢?

这里的解释也是通过假设法,然后演绎,有些烧脑,小伙伴们坚持住:

假设第一次扔在第x+1层(比x大):

如果第一个鸡蛋碎了,那么第二个鸡蛋只能从第1层开始一层一层扔,一直扔到第x层。

这样一来,我们总共尝试了x+1次,和假设尝试x次相悖。由此可见,第一次扔的楼层必须小于x+1层。

假设第一次扔在第x-1层(比x小):

如果第一个鸡蛋碎了,那么第二个鸡蛋只能从第1层开始一层一层扔,一直扔到第x-2层。

这样一来,我们总共尝试了x-2+1 = x-1次,虽然没有超出假设次数,但似乎有些过于保守。

假设第一次扔在第x层:

如果第一个鸡蛋碎了,那么第二个鸡蛋只能从第1层开始一层一层扔,一直扔到第x-1层。

这样一来,我们总共尝试了x-1+1 = x次,刚刚好没有超出假设次数。

因此,要想尽量楼层跨度大一些,又要保证不超过假设的尝试次数x,那么第一次扔鸡蛋的最优选择就是第x层。

归纳

如果第一次扔鸡蛋没有碎,我们的尝试消耗了一次,问题就转化成了两个鸡蛋在100-x层楼往下扔,要求尝试次数不得超过x-1次

所以第二次尝试的楼层跨度是x-1层,绝对楼层是x+(x-1)层

同理,如果鸡蛋还没有碎,第三次楼层跨度是x-2,第四次是x-3

根据总结,可以列出一个楼层数的方程式:

x + (x-1) + (x-2) + ... + 1 = 100

下面我们来解这个这个方程:

(x+1)*x/2 = 100

最终x向上取整,得到 x=14

因此,最优解在最坏情况的尝试次数是14次,第一次扔鸡蛋的楼层也是14层。

最后,让我们把第一个鸡蛋没碎的情况下,所尝试的楼层数完整列举出来:

14,27, 39, 50, 60, 69, 77, 84, 90, 95, 99, 100

 

这个问题进阶一点的问法就是leetcode上887题,

K 个鸡蛋,并可以使用一栋从 1 到 N  共有 N 层楼的建筑。
每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。
你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。
每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。
你的目标是确切地知道 F 的值是多少。
无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?

这个题,鸡蛋不再是2个,楼层也不确定,一般使用动态规划来解决。

假设我们第一个鸡蛋扔出的位置在第X层(1<=X<=M),会出现两种情况:

1.第一个鸡蛋没碎

那么剩余的M-X层楼,剩余N个鸡蛋,可以转变为下面的函数:

F(M-X,N)+ 1,1<=X<=M

2.第一个鸡蛋碎了

那么只剩下从1层到X-1层楼需要尝试,剩余的鸡蛋数量是N-1,可以转变为下面的函数:

F(X-1,N-1) + 1,1<=X<=M

整体而言,我们要求出的是 N层楼 / K个鸡蛋 条件下,最大尝试次数最小的解,所以这个题目的状态转移方程式如下:

X可以为1......N,所以有M个Max( F(N-X,K)+ 1, F(X-1,K-1) + 1)的值,最终F(N,K)是这M个值中的最小值,即最优解

F(N,K)= Min(Max( F(N-X,K)+ 1, F(X-1,K-1) + 1)),1<=X<=N

代码:

class Solution {
    public int superEggDrop(int K, int N) {//k=eggs,N=floor
        int[][] dp = new int[K+1][N+1];
        for (int i=0;i<=N;i++) {
            //首先是eggs=0的情况
            dp[0][i] = 0;
            //然后是eggs=1的情况
            //eggs=1的时候,肯定是从第0层一直往上实验
            dp[1][i] = i;
        }
        //再考虑floors的边界
        for (int i=1;i<=K;i++) {
            //首先是floors=0的情况
            dp[i][0] = 0;
            //然后是floors=1的情况
            dp[i][1] = 1;
        }

        //状态转移方程dp[m][n] = min(max(dp[m-1][n-1]+1,dp[m][n-1]+1))
        for(int i=2;i<N+1;i++){
            for(int j=2;j<K+1;j++){
                int result = Integer.MAX_VALUE;
                for (int drop=1;drop<=i;drop++) {            
                    int broken = dp[j - 1][drop - 1];
                    int unbroken = dp[j][i- drop];
                    int condition = Math.max(broken, unbroken) + 1;
                    result = Math.min(condition, result);
                }
                dp[j][i] = result;
            }
        }
        return dp[K][N];
    }
}

这个解法复杂度还是不满足要求的,将继续寻找更好的方法。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值