问题:
现有2个鸡蛋硬度相同,100层的楼房。鸡蛋从某一层楼房扔下时,可能会破碎。为找出使鸡蛋破碎不破碎的最高楼层f(0<=f<=100),需要做x次实验,求x的最小值。
扩展问题:
先有M个鸡蛋,N层楼房,为找出使鸡蛋不破碎的最高楼层,需要做x次实验,求x的最小值。
问题分析:
这个问题刚拿到时,很容易想到用二分法求解,因为看上去很像有序集合的查找。确实,若鸡蛋数量足够多,二份法是最快的求解方式,需要log2100向上取整次。若鸡蛋在f层破碎,则在[1,f-1]区间查找,若不破碎则在[f+1,100]区间内求解。但是,当鸡蛋数量小于二分法所需的数量时,就不能用二份法求解。特别地,当只有一个鸡蛋时,应该从1层向上逐渐测试每一层。
从测试的第一步开始考虑,此时有两个鸡蛋且未做任何实验。第一次在f1层进行实验,可能有两种结果,破碎或不破碎。若鸡蛋在f1层破碎,则剩下的问题是在[1, f1-1]层中查找,且只剩1个鸡蛋;若鸡蛋在f1层不破碎,则剩下的问题是在[f1+1,100]层中查找,且剩下2个鸡蛋。可以看出,子问题有两个影响因素,所剩楼层及所剩鸡蛋。
定义f(n,m)为有m个鸡蛋,n层楼房,需要的最小实验次数。已知f(0,m) = 0,根据之前的分析又有f(n, 1) = 1。可以写出递归公式。
f(n,m) = min(i from 1 to n) ( max( f(i-1, m-1), f(n - i - 1, m)) ) + 1
可以利用动态规划求解。
以15层2个鸡蛋为例求解。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
2 | 0 | 1 | 2 | 2 | 3 | 3 | 3 | 3 | 4 | 4 | 4 | 5 | 5 | 5 | 5 | 5 |
利用该递归关系,可以解出100楼2个鸡蛋最少需要14次测试。
代码示例:
import sys
g_res_table = []
def Compute(floor, egg):
global g_res_table
if floor <= 0 or egg <= 0:
return -1
# initial result table
for i in range(0, floor + 1):
lst_floor = []
for j in range(0, egg + 1):
lst_floor.append(0)
g_res_table.append(lst_floor)
for i in range(1, egg + 1):
g_res_table[1][i] = 1
g_res_table[0][i] = 0
# initial value
for i in range(0 ,floor + 1):
g_res_table[i][1] = i
for i in range(2, floor + 1):
for j in range(2, egg + 1):
tmp_min = -1
for cur in range(1, i + 1):
if g_res_table[cur - 1][j - 1] > g_res_table[i - cur][j]:
max_value = g_res_table[cur - 1][j - 1]
else:
max_value = g_res_table[i - cur][j]
if max_value < tmp_min or tmp_min == -1:
tmp_min = max_value
g_res_table[i][j] = 1 + tmp_min
return g_res_table[floor][egg]
def main():
floor = int(sys.argv[1])
egg = int(sys.argv[2])
res = Compute(floor, egg)
print res
if __name__ == '__main__':
main()