LeetCode(887):鸡蛋掉落 Super Egg Drop(Java)

本文介绍了LeetCode的887题——鸡蛋掉落问题的解法,包括递归、二分查找和动态规划的优化策略。通过动态规划,自底向上和自顶向下两种方式解决问题,最后给出了数学法的最优解。
摘要由CSDN通过智能技术生成

2019.11.27 LeetCode 从零单刷个人笔记整理(持续更新)

github:https://github.com/ChopinXBP/LeetCode-Babel

这是一道经典的智力题。由于有鸡蛋摔碎的限定条件,因此不能依靠单纯的二分查找来解决,可以从递归开始,不断优化和升级方法。

1.递归(超时)

在手握K个鸡蛋时从第i层楼[0,N]向下丢鸡蛋,会有两种可能:

1.鸡蛋摔碎,将剩余K-1个鸡蛋移动到楼[0,i-1]进行测递归测试。

2.鸡蛋没碎,将剩余K个鸡蛋移动到楼[i,N]进行测试,可将i层看成0层,对[0,N-i]进行递归测试。

递归公式

result = min(max(superEggDrop(K - 1, i - 1), superEggDrop(K, N - i)) + 1)

2.动态规划:二分搜索

观察递归公式

result = min(max(superEggDrop(K - 1, i - 1), superEggDrop(K, N - i)) + 1)

其中i在[1.N]变化。

而fun1=superEggDrop(K - 1, i - 1)随i单调增,fun2=superEggDrop(K, N - i)随i单调减,两条单调函数的交点即为两者最大值的最小取值点。

因此可以对i在[1,N]上进行二分查找。在二分查找过程中,可以设计一个哈希表避免重复计算。

3.动态规划:自底向上

观察转为动态规划的递归公式

dp[i][j] = min(max(dp[i-1][loc-1], dp[i][j-loc]) + 1)

其中i在[1.j]变化,j在[1, N]变化。

而fun1=dp[i-1][loc-1]随loc单调增,fun2=dp[i][j-loc]随loc单调减,两条单调函数的交点即为两者最大值的最小取值点。

寻找loc的思路转换为while循环遍历查找。

令left为loc-1时的函数值,right为loc时的函数值,循环遍历找到最小值的拐点。由于后序j值上限不断增大,loc拐点值只可能逐渐向右移动,以此可以简化后序查找。

3.动态规划(超时):求能够测得的最少楼层

dp[i][j]代表有i个鸡蛋和j次移动时一定能够测得的最少楼层数。没有鸡蛋时或没有移动次数时无法测试dp=0;只有1个鸡蛋但有j次移动,dp=j(从0层开始逐层测试)。

有i个鸡蛋和j次移动时,前j-1次移动的测试结果可能由多种测试路径得来,将其划分为k次和(j-1)-k次遍历取最大。第j-1次移动鸡蛋碎的结果为dp[i-1][k],鸡蛋未碎的结果为dp[i][(j - 1) - k],当前楼层第j次移动的最少测试结果为1(摔碎)。

dp[i][j] = max(dp[i - 1][k], dp[i][(j - 1) - k]) + 1

4.动态规划(次优):求能够测得的最多楼层

dp[i][j]代表i个鸡蛋在j次移动内能测试确定的最多楼层数。

从第一层楼开始更新dp数组。0次移动无法进行测试,初始值dp[i][0]=0。

每多一次移动,可以多进行一次测试。在手中有i-1个鸡蛋的第j-1次移动时已经测出楼高dp[i-1][j-1],一次移动最差情况也可以测出当前楼层的情况,因此逆推出当前能够保证第j次移动得出结果的测试楼层为dp[i-1][j-1]+1。

第j次移动鸡蛋摔碎,可以确定当前层(1层)的结果(碎),剩余i-1个鸡蛋和j-1次移动能得出dp[i-1][j-1]层。

h1 = dp[i][j] = dp[i-1][j-1] + 1;

第j次移动鸡蛋未碎,可以确定前dp[i-1][j-1]+1层的结果(不碎),剩余i个鸡蛋和j-1次移动能得出dp[i][j-1]层。

h2 = dp[i][j] = dp[i-1][j-1] + 1 + dp[i-1][j];

综合结果:

dp = h2 >= max(h1, h2)

5.数学法(最优)

最差情况下(一个鸡蛋)用N次移动可以测得N层楼高,因此可以移动次数x必在[1,N]之间,可以对x进行二分查找。

递归函数fun(x)代表x次移动能够测试的最大楼高。

求解思想链接

函数推导链接


传送门:鸡蛋掉落

You are given K eggs, and you have access to a building with N floors from 1 to N.

Each egg is identical in function, and if an egg breaks, you cannot drop it again.

You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break, and any egg dropped at or below floor F will not break.

Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N).

Your goal is to know with certainty what the value of F is.

What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F?

你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N 共有 N 层楼的建筑。

每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。

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

每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。

你的目标是确切地知道 F 的值是多少。

无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?

示例 1:
输入:K = 1, N = 2
输出:2
解释:
鸡蛋从 1 楼掉落。如果它碎了,我们肯定知道 F = 0 。
否则,鸡蛋从 2 楼掉落。如果它碎了,我们肯定知道 F = 1 。
如果它没碎,那么我们肯定知道 F = 2 。
因此,在最坏的情况下我们需要移动 2 次以确定 F 是多少。

示例 2:
输入:K = 2, N = 6
输出:3

示例 3:
输入:K = 3, N = 14
输出:4

提示:
1 <= K <= 100
1 <= N <= 10000


import java.util.HashMap;

/**
 *
 * You are given K eggs, and you have access to a building with N floors from 1 to N.
 * Each egg is identical in function, and if an egg breaks, you cannot drop it again.
 * You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break,
 * and any egg dropped at or below floor F will not break.
 * Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N).
 * Your goal is to know with certainty what the value of F is.
 * What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F?
 * 你将获得 K 个鸡蛋,并可以使用一栋从 1 到 N  共有 N 层楼的建筑。
 * 每个蛋的功能都是一样的,如果一个蛋碎了,你就不能再把它掉下去。
 * 你知道存在楼层 F ,满足 0 <= F <= N 任何从高于 F 的楼层落下的鸡蛋都会碎,从 F 楼层或比它低的楼层落下的鸡蛋都不会破。
 * 每次移动,你可以取一个鸡蛋(如果你有完整的鸡蛋)并把它从任一楼层 X 扔下(满足 1 <= X <= N)。
 * 你的目标是确切地知道 F 的值是多少。
 * 无论 F 的初始值如何,你确定 F 的值的最小移动次数是多少?
 *
 */

public class SuperEggDrop {
   

    //递归(超时)
    public int superEggDrop2(int K, int N) {
   
        if(N < 2 || K == 1){
   
            return N;
        }
        int result = N;
        //在手握K个鸡蛋时从第i层楼[0,N]向下丢鸡蛋,会有两种可能:
        //1.鸡蛋摔碎,将剩余K-1个鸡蛋移动到楼[0,i-1]进行测递归测试。
        //2.鸡蛋没碎,将剩余K个鸡蛋移动到楼[i,N]进行测试,可将i层看成0层,对[0,N-i]进行递归测试。
        for(int i = 1; i <= N; i++){
   
            int curMin = Math.max(superEggDrop2(K - 1, i 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值