[动态规划] 谷歌鸡蛋问题
问题:
现有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()