鸡蛋掉落问题解析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了鸡蛋掉落问题解析相关的知识,希望对你有一定的参考价值。

参考技术A 原始题目来源于LeetCode https://leetcode-cn.com/problems/super-egg-drop/comments/

第一反应,二分法。但是鸡蛋数量是有限的。比如K=2,N=6的情况。第一次先扔3楼,3楼如果没碎,则在4-6楼中继续试验;3楼碎了的话,则只能从1楼开始进行,最多次数3。
不过,不适合用于鸡蛋数量少,楼层高的情况。比如K=2,N=50的情况。第一次扔25楼,如果碎了,就必须从1楼还是扔。则最多有25次。如果第一次扔10楼,不碎的话扔20楼,一直到40楼,这样最多的次数为4+10=14次。可见,直接二分的方法不通用。按照几等分的方法也不通用。

第二反应,动态规划。和背包问题有些像,也是基于上一个条件来得出最优解。本题的关键点就转换为找到状态转移方程,以及结束条件。

使用 dp(K,N) 表示状态转移,表示在有 K 个鸡蛋,N楼时候需要扔鸡蛋的次数。如果在第 i 层扔鸡蛋。

状态的终止条件:

由于不知道起始扔鸡蛋的位置,所以在设置起始位置时候,需要遍历来进行查找。
所以 动态规划的时间复杂度 = N * dp(K,N),其中 dp 为一个循环,即O(N),最后得到的复杂度为 O(N^2)。

egg_drop.c

Makefile:

执行效果
./egg_drop
superEggDrop ret:4

leetcode困难887鸡蛋掉落


思路1:递归

N :使用一栋从 1 到 N 共有 N 层楼的建筑
F :满足 0 <= F <= N ,任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破(F 比 N 多一个 0 层)

问题转换:

将问题从: N 个楼层,有 K 个蛋,求最少要扔 T 次,才能保证当 F 无论是 0 <= F <= N 中哪个值,都能测试出来
转变为:有 K 个蛋,扔 T 次,求可以确定 F 的个数,然后得出 N 个楼层

  • 1个蛋 T 次机会,或 K 个蛋 1 次机会,只可以确定出 T + 1 个 F(去掉0层,即最多可以确定第 T 楼)
  • 其他情况时,递归。【蛋碎了减 1 个,机会减 1 次】 + 【蛋没碎,机会减 1 次】

比如: N = 2 层楼(只需要K(1) 个蛋,扔T(2)次)
在 1 层扔,碎了,F < 1,所以确定 F = 0
在 1 层扔,没碎,但在 2 层扔,碎了, F >= 1 && F < 2,所以确定 F = 1
在 2 层扔,没碎,F >= 2,所以确定 F = 2

比如:K(2) 个蛋,T(3)次机会

  • 第一次应当从 3 层仍,最坏情况碎了,剩下 K(1) 个蛋,T(2)次机会,可以判断一二层的情况;
  • 第二次从 5 层扔,碎了的话,剩下 K(1) 个蛋,T(1)次机会,也可以判断 T+1=2 个楼层的情况,即三四层情况
  • 如果还没碎,剩下 K(2) 个蛋,T(1)次机会,只能从 6 层扔,判断T+1=2 个楼层的情况,即五六楼情况
class Solution {
    public int superEggDrop(int k, int n) {
        int T=1;
        while(calF(k,T)<n+1) T++;
        return T;
    }

    public int calF(int K,int T){
        if (T == 1 || K == 1) return T + 1;
        return calF(K - 1, T - 1) + calF(K, T - 1);
    }
}

优化:动态规划,避免递归重复计算
左边是碎的那段 长度是dp[k][T - 1]
右边是没碎的那段 长度是dp[k-1][T - 1] 因为已经碎了一个了
中间是我选定扔的楼层 是1

class Solution {
    public int superEggDrop(int k, int n) {
        int[][]dp=new int[k+1][10000];
        int T=0;
        while(dp[k][T]<n){
            T+=1;
            for(int i=1;i<=k;i++){
                dp[i][T]=1+dp[i-1][T-1]+dp[i][T-1];
            }
        }
        return T;
    }
}

再优化:dp[i][T]=1+dp[i-1][T-1]+dp[i][T-1];里的[T-1]是相同的,所以可以忽略这一维,如果采用k倒着从大到小计算 就可以只存一行的dp[k] 直接原地更新dp[k] 不影响后续计算

class Solution {
    public int superEggDrop(int k, int n) {
        int[]dp=new int[k+1];
        int T=0;
        while(dp[k]<n){
            T+=1;
            for(int i=k;i>0;i--){
                dp[i]=1+dp[i-1]+dp[i];
            }
        }
        return T;
    }
}

以上是关于鸡蛋掉落问题解析的主要内容,如果未能解决你的问题,请参考以下文章

鸡蛋掉落

leetcode困难887鸡蛋掉落

力扣第887题 鸡蛋掉落

每日5题鸡蛋掉落

LeetCode887鸡蛋掉落——dp

[LeetCode] 887. 鸡蛋掉落