The Two Egg Problem

曾经是Google的一道经典题。

题意:有一个百层高楼,鸡蛋在\(L\)层及以下扔都不碎,在\(L\)层以上都会碎。现在某人有\(k\)个鸡蛋,问在最坏情况下,至少扔多少次(用\(m\)表示)可以确定\(L\)的值。

分析:先来考虑\(k=1\)的情况。只有1个鸡蛋,为了得到一个确定的\(L\),只能从第一层开始,逐渐尝试增加楼层高度,因此\(m=100\)时,无论\(L\)的值是多少,都可以被确定。

再来考虑\(k=\infty\)的情况。这种情况就变为了binary search的问题,先拿一个在50层扔,如果碎,则在25层扔;如果不碎,则在75层扔...即\(m=7\)。

最后来考虑\(k=2\)的情况。因为\(k=1\)只能一层一层试,所以第一个鸡蛋应该尽可能缩小搜索空间,但是如果第一个鸡蛋的楼层间隔太小(比如在2层、4层...),无疑会增加\(m\)。不妨取第一个鸡蛋在10层、20层...,共10次;假如第一个在10层没碎,在20碎了,那么第二个鸡蛋可以尝试11、12...19,共9次;故\(m=19\)。

上面方案的问题在于:如果临界楼层比较高,那么第二个鸡蛋的次数是确定的,但第一个就需要多试几次,总次数就会增加。

那么如何使得不论临界楼层在哪,\(m\)的值都不会波动呢?

很简单,只要第一个多扔一次,确定的范围(第二个要试的次数)减小,总次数就会均衡。

对于第一个鸡蛋,第一次在\(a\)层扔,如果不碎,第二次向上增加\(a-1\)层...直到最后只向上增加\(1\)层:\(a+(a-1)+...+1\geq100\),故\(a\geq13.7\)。

鸡蛋一:在14层、27层、39层、50层、60层、69层、77层、84层、90层、95层、99层、100层扔,共12次;

鸡蛋二:如果蛋一在14层碎了,蛋二要扔13次,共14次;如果蛋一在27层碎了,蛋二要扔12次,共14次...故\(m=14\)。

Super Egg Problem

对于\(k=2\),我们有了一个比较好的解决方案。那么现在有\(k\)个鸡蛋,楼高\(n\)层,问题(记作\(m(k,n)\))又该如何解决?

\(k=1\)和\(n=1\)的情况比较简单:

n\k 1 2 3 4 ...
1 1 1 1 1
2 2
3 3
...

那么如果我们递归地思考:任选一层\(h\)扔第一个鸡蛋,无非有碎和不碎2种情况:

碎:临界楼层在1~h之间,问题规模缩小为\(m(k-1,h-1)\);

不碎:临界楼层在h~n之间,问题规模缩小为\(m(k,n-h)\)。

所以:\(m_h(k,n)=1+max\{ m(k-1,h-1),m(k,n-h)\}\)。

对于\(h\),可以采用枚举的方法,计算\(m_h(k,n)\),在其中选出一个最小的值,故问题得到解决:

\[m(k,n)=min\{m_h(k,n)\},h=1,2,...n
\]

Base Case就是\(k=1\)和\(n=1\)。

时间复杂度\(O(KN^2)\),空间复杂度\(O(KN)\)。

记忆化递归:

class Solution {
public:
int superEggDrop(int K, int N) {
vector<vector<int>> memo(K + 1, vector<int>(N + 1, 0));
return helper(K, N, memo);
}
private:
int helper(int K, int N, vector<vector<int>>& memo) {
if(K == 1) {
return N;
}
if(N <= 1) {
return N;
}
if(memo[K][N]) {
return memo[K][N];
} int ans = INT_MAX;
for(int i = 1;i <= N;++i) {
ans = min(ans, 1 + max(superEggDrop(K - 1, i - 1), superEggDrop(K, N - i)));
}
memo[K][N] = ans;
return ans;
}
};

上述最直观的解法复杂度太高,无法通过Leetcode的数据。如何优化呢?

\(m(K,N)\)表示该问题的解,如果鸡蛋数\(K\)固定,随着楼层数\(N\)增加,问题的解一定是增加的。

我们又把问题分解为了2个子问题\(m(k-1,h-1)\)和\(m(k,n-h)\),\(m(k-1,h-1)\)随\(h\)单调递增,\(m(k,n-h)\)单调递减(图源):



此时\(min(max(...))\)就是求中间的折点,可以使用Binary Search,时间复杂度降为\(O(KNlgN)\):

class Solution {
public:
int superEggDrop(int K, int N) {
vector<vector<int>> memo(K + 1, vector<int>(N + 1, 0));
return helper(K, N, memo);
}
private:
int helper(int K, int N, vector<vector<int>>& memo) {
if(K == 1) {
return N;
}
if(N <= 1) {
return N;
}
if(memo[K][N]) {
return memo[K][N];
} int ans = INT_MAX;
int l = 1, r = N + 1;
while(l < r) {
int m = l + (r - l) / 2;
int broken = helper(K - 1, m - 1, memo), noBroken = helper(K, N - m, memo);
if(broken > noBroken) {
r = m;
ans = min(ans, 1 + broken);
}
else {
l = m + 1;
ans = min(ans, 1 + noBroken);
}
} memo[K][N] = ans;
return ans;
}
};

当然,此题还有\(O(KN)\)的做法,甚至还有一种数学做法可以达到\(O(KlgN)\)的时间复杂度和\(O(1)\)的空间复杂度,由于本人水平实在有限,就不再探索。

Egg Dropping Puzzle的更多相关文章

  1. Egg Dropping Puzzle问题的分析

    首先,基本问题是这样:You are given two eggs, and access to a 100-storey building. The aim is to find out the h ...

  2. 扔鸡蛋问题具体解释(Egg Dropping Puzzle)

    经典的动态规划问题,题设是这种: 假设你有2颗鸡蛋,和一栋36层高的楼,如今你想知道在哪一层楼之下,鸡蛋不会被摔碎,应该怎样用最少的測试次数对于不论什么答案楼层都可以使问题得到解决. 假设你从某一层楼 ...

  3. 扔鸡蛋问题详解(Egg Dropping Puzzle)

    http://blog.csdn.net/joylnwang/article/details/6769160 经典的动态规划问题,题设是这样的:如果你有2颗鸡蛋,和一栋36层高的楼,现在你想知道在哪一 ...

  4. 动态规划法(六)鸡蛋掉落问题(一)(egg dropping problem)

      继续讲故事~~   这天,丁丁正走在路上,欣赏着路边迷人的城市风景,突然发现前面的大楼前围了一波吃瓜群众.他好奇地凑上前去,想一探究竟,看看到底发生了什么事情.   原来本市的一位小有名气的科学家 ...

  5. 2 Egg Problem

    继续我们的推理问题之旅,今天我们要对付的是一个Google的面试题:Two Egg Problem. 我们开始吧! No.2  Google Interview Puzzle : 2 Egg Prob ...

  6. 【LeetCode】887. Super Egg Drop 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 参考资料 日期 题目地址:https://leetc ...

  7. ERROR 1010 (HY000): Error dropping database (can't rmdir './test/', errno: 17)

    在删除数据库的时候报标题所示错误 mysql> drop database test; ERROR (HY000): Error dropping database (can't rmdir ' ...

  8. Puzzle 面向服务/切面(AOP/IOC)开发框架 For .Net

    Puzzle 面向服务/切面AOP开发框架 For .Net AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效 ...

  9. HDU5456 Matches Puzzle Game(DP)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5456 Description As an exciting puzzle game for ...

随机推荐

  1. 一键创建以太坊ERC20代币教程

    30秒极速创建以太坊ERC20代币 傻瓜式创建,界面化创建,自动创建代币,简单好用 合约采用新版 5.10 新版合约编译器,合约代码100%安全无任何漏洞 下面是详细的使用和创建教程 1.点击创建ER ...

  2. Python常见数据结构-字符串

    字符串基本特点 用引号括起来,单引号双引号均可,使用三个引号创建多行字符串. 字符串不可变. Python3直接支持Unicode编码. Python允许空字符串存在,不含任何字符且长度为0. 字符串 ...

  3. CVE-2020-1938:Apache-Tomcat-Ajp漏洞-复现

    0x00 漏洞简介 Apache与Tomcat都是Apache开源组织开发的用于处理HTTP服务的项目,两者都是免费的,都可以做为独立的Web服务器运行. Apache Tomcat服务器存在文件包含 ...

  4. Scanner的小细节

    Scanner对象是用来接收键盘输入的数据的,可以接收字符串,数字,浮点数,大数等多种数据. Scanner scanner = new Scanner(System.in); System.out. ...

  5. iNeuOS工业互联平台,部署在智能硬件网关,实现了从边缘端到云端的一体化部署

    目       录 1.      概述... 2 2.      平台演示... 3 3.      智能硬件网关配置(参考)... 3 4.      iNeuOS在网关中的部署步骤... 5 4 ...

  6. x86汇编之栈与子程序调用

    什么是栈 栈与普通数据结构所说的栈的概念是相似的,遵循后进先出原则.不同的是汇编中所说的栈是一个在内存中连续的保存数据的区域,也即是实际存在的内存区域,进栈和出栈遵循后进先出原则. 在x86架构中,栈 ...

  7. 发现一款手绘可视化神器!Python绘图还在用Matplotlib?out了 !

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取htt ...

  8. Daily Scrum 12/16/2015

    Process: Dong&Minlong : 继续对Oxford Speech 接口进行调试,并且完成了相应工作的转接. Yandong@Zhaoyang: 完成了对一些Bug的修复工作,程 ...

  9. Git敏捷开发--rebase命令

    git rebase是git下比较常用的命令,以下记录自己遇到较多的使用场景. 合并分支 在多人协作的项目中,拉分支是很常见的事情,经常需要同步自己的分支与远端master分支一致,有两种方式: gi ...

  10. lua使用笔记1:Linux 中安装lua

    1.lua安装 1)http://www.lua.org/download.html为下载页面 linux中运行 wget http://www.lua.org/ftp/lua-5.2.3.tar.g ...