Two eggs problem可以说是互联网面试中老生常谈的算法题了,经常可以在各大互联网公司的笔试真题中看到它的各类变种(腾讯大厦,球掉落问题,玻璃珠问题等等)。本文将深入探讨此类问题及其延伸问题的通用解法,并给出javascript代码实现。
目录
问题描述
Two-eggs problem
首先看下游戏的定义:
有一幢100层的大楼,玩家可以到达大楼的任意楼层。现给玩家两个一模一样的鸡蛋,当玩家拿着鸡蛋从某一楼层扔下时,一定会出现两个结果,鸡蛋碎了或者没碎(完好无损)。该大楼有一个临界楼层,低于它的楼层往下扔鸡蛋时鸡蛋不会碎,等于或高于它时会碎。请为该玩家设计一种丢鸡蛋策略,使得该策略下的最坏情况的次数比其他任何策略最坏的次数都少。输出该策略下的最坏情况次数。
很多人乍一看到这道题会很懵,什么叫丢鸡蛋策略?怎么设计?如果用代码给出设计方案?你可能会去把问题分解成第一次在哪个楼层扔,鸡蛋会不会碎,还剩几个鸡蛋,第二次在哪个楼层扔,鸡蛋会不会碎,还剩几个鸡蛋,第三次……然后你就会觉得这是一个特别复杂/抽象的问题,然后就放弃了。
所以此类问题我们要换个思路去想,这里先抛开那些经典的算法思想,总结下解题思路:
- 分解为若干子问题
- 化抽象为具体,不妨代入未知数,先进入子问题再说
- 简化问题求解,比如将2个鸡蛋改成1个鸡蛋,100层楼改成10层楼
- 根据上面两步,确定最终使用的方法(动态规划?回溯?分治)
有了以上的思路,我们很容易想到,用递归的思想去解决,如何递归呢?这里我们暂时卖下关子,看一下此类问题的通用表示方法:n-eggs problem
扩展到 n-eggs problem
现在有m层楼(m>1),玩家手里有n个鸡蛋,求设计一种方法,使得在该方法下找到临界楼层的最坏情况次数最少。
这个问题可以抽象为这样一个函数:
函数入口是楼层数和鸡蛋数,返回最优方案的最坏次数(throwNumbers of the worst case of the best strategy)。
在下文中我们不妨用 Tn(f,e) 来表示f层楼e个鸡蛋的输出结果。
解决方案
回到刚才讨论的解决思路,我们不妨分别考察下一个鸡蛋,两个鸡蛋,三个鸡蛋的 Tn(f,e) 求法。
one-eggs problem
这类问题是不是很简单?因为你就只有一个鸡蛋,所以为了保证一定能找到临界楼层,我们只能从第一层开始遍历楼层,所以最坏情况的次数是楼层f, 那么可以得到:
Tn(f,1)=f
two-eggs problem
我们不妨设 αi 为第i次扔蛋选择的楼层。每次扔蛋有两种结果:
- 鸡蛋碎了,那么临界楼层一定在 αi 层及以下楼层范围内,问题转移为1个蛋, αi -1层楼的问题,即 Tn(αi−1,1)
- 鸡蛋没碎,那么临界楼层一定在 αi 层以上,问题转移为2个蛋,100- αi 层楼的问题,即 Tn(100−αi,2)
可以用以下流程图表示: