鸡蛋掉落问题

鸡蛋掉落问题

链接: https://leetcode-cn.com/problems/super-egg-drop/solution/ji-dan-diao-luo-by-leetcode/

题干

你将获得 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

分析

化繁就简,我们先不考虑鸡蛋的数量,给定楼层N,得出安全层F,最简单的解法就是二分法。
不妨设N=16,F=2,即一共16层楼,安全层在第2层。
具体思路是:
Step1:(1+16)/2 = 8,即在1-16层楼的中间楼层,第8层扔鸡蛋,破了,说明F在1-8层。
Step2:(1+8)/2 =4,即在1-8层的中间楼层,第4层扔鸡蛋,破了,说明F在1-3层。
Step3:(1+3)/2 =2,即在1-3层的中间楼层,第2层扔鸡蛋,没破,说明F在2-3层。
Step4:在第3层扔鸡蛋,破了,说明F=2,即安全层是第2层。

以上就是不考虑鸡蛋数量的二分法思路。那让我们加上鸡蛋的限制,本题就有了鸡蛋数量和未知楼层数两个变量,可以用动态规划的思路求解,把鸡蛋和楼层当做状态输入,得出最小移动次数。状态可以表示成 (K, N): K 为鸡蛋数和 N 为楼层数。当从第 X 楼扔鸡蛋的时候,要么鸡蛋不碎,状态变成 (K, N-X),或者碎掉,状态变成 (K-1, X-1)。

在这里插入图片描述

本题需要找到最小移动次数,所以最外层是min,同时,无论F初始值为多少,即考虑到最坏情况,所以里面是max,意思要是鸡蛋摔坏所需要的次数更多,那就拿它摔坏来考虑。

具体实现

		// 根据提示N和K至少相差100倍,且N最大不过10000。
		// 可以通过N*100+K把两个值存在一个int里,且相互独立。
		// 如 N=5321,K=82,N*100+K= 542182,前四位是N的值,后两位是K的值,
		// 共同组成一个key,只占用一个int数。
        if (!memo.containsKey(N * 100 + K)) {
            int ans;
            // 未知楼层为0则不用扔了。
            if (N == 0)
                ans = 0;
                // 只有一个鸡蛋,只能老老实实从下往上扔,
                // 最坏情况是F=N,要扔N次。
            else if (K == 1)
                ans = N;
            else {
            // low和high分别代表上下两个光标,框住的这部分代表未知楼层。
                int low = 1, high = N;
                // 当这两个光标贴在一起的时候,代表已经穷尽所有楼层。
                while (low + 1 < high) {
                    // 二分法查找
                    int x = (low + hi) / 2;
                    // t1和t2分别代表摔坏和没摔坏两种情况所需要的最小移动次数
                    int t1 = dp(K-1, x-1);
                    int t2 = dp(K, N-x);
					
					//考虑最坏情况,所以选t1和t2中较大的那个情况。
                    if (t1 < t2)
                        low = x;
                    else if (t1 > t2)
                        high = x;
                    else
                        low = high = x;
                }

				// 比较从low这一层开始扔和从high这一层开始扔哪个次数更少。
                ans = 1 + Math.min(Math.max(dp(K-1, low-1), dp(K, N-low)),
                                   Math.max(dp(K-1, high -1), dp(K, N-high)));
            }
			// 无论哪种扔法,都可能出现重复的状态,所以存在memo里面以便复用。
            memo.put(N * 100 + K, ans);
        }

        return memo.get(N * 100 + K);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值