清晰解题:扔鸡蛋问题

版权声明:本文为博主学习笔记, 注明来源情况下随意转载 https://blog.csdn.net/lengxiao1993/article/details/52551492

一幢 100 层的大楼,给你两枚鸡蛋。假设,在第 n 层扔下鸡蛋,鸡蛋不碎,那么从第 n-1 层扔鸡蛋,鸡蛋也不会碎。两个鸡蛋一模一样,不碎的话可以扔无数次。目标是利用这两个鸡蛋找出临界楼层 t , 使得鸡蛋从 t 层扔下不会碎, 从 t+1 层扔下会碎。

现要求回答, 最少需要多少次尝试, 才能保证在最坏的情况下,找到楼层 t , 且需要给出尝试的策略。

  • 明确问题
    • 题目要求是考虑 在策略可能遭遇最坏情况下尝试次数最少。 这个地方有些绕,但是必须搞清楚。

    • 举个例子, 如果你用二分法,第一次把鸡蛋从50层扔下去, 最坏的情况是临界层在49层, 第一个鸡蛋碎了,第二个鸡蛋必须得从第一层楼找起。 因此这种策略最坏情况下的尝试次数是50次, 如果你决定采用这个策略, 那你针对题目的答案就是50

    • 现在的目标是找到一个最优策略, 该策略所对应的最坏情况, 比其他策略所对应的最坏情况所需要尝试的次数都要少

明确了问题以后,首先 可以获得一个直觉性的推论, 那就是, 最优的策略必然是用第一个鸡蛋先进行跳跃性的尝试, 如果中间碎了, 就可以用第二个鸡蛋,在剩余的范围内,锁定答案

  • 1.数学猜想反推法
    • 假设题目所要求的策略存在,该策略可以保证在最坏情况下至多需要扔x次鸡蛋就可以找出临界楼层

    • 此时可以将问题转化为:如果只允许扔x次鸡蛋 能确定的最高的楼层是多少。

    • 现在思考, 第一次鸡蛋应该从哪个楼层扔下?

      • 因为此时我们有两个鸡蛋, 为了在有限次数内检查尽可能高的楼层。 第一次扔的时候,我们会希望能尽可能高一些 如果鸡蛋没碎,我们就能排除掉越多的楼层。 但是很显然, 不能过高, 因为万一鸡蛋碎了, 第二个鸡蛋就只能从第一层开始尝试起。

      • 那么第一次扔的楼层最高可以选几呢?

      • 是x, 因为我们总要考虑最坏情况, 当我们从x层扔下的时候, 最坏的情况有可能是临界层为x-1, 此时就需要尝试x次,才能找到这个临界层。 如果第一次尝试的楼层比x大, 显然会导致最大尝试次数超过x , 不符合我们的假设。

    • 回到我们的问题, 现在知道了在最大尝试次数为x次时, 最优策略第一次必然只能从x层扔下的推论后, 我们最大能检查的楼层是多少。

      • 由于第一次从x层扔下时, 我们已经用掉了一次尝试机会, 此时只能再尝试x-1次。 这样我们可以第二次尝试的楼层数可以向上增加x-1层, 即为 x + (x-1).

      • 如果再高,例如第二次我们从2x层扔下, 我们在最坏情况下(临界楼层为2x-1),需要尝试的次数会变成x+1, 不符合我们的假设。

      • 这样依次类推, 第三次向上的增量就只能为 x-3, 第四次向上的增量就只能为 x-4, …, 最后一次向上的增量为 1

      • 此时我们可以得出,如果只能尝试x次, 我们能检测的最高楼层为 x+(x1)+(x2)+(x3)....+1=(x(x+1))/2x+ (x-1) +(x-2) + (x-3)....+1 = (x*(x+1))/2

此时答案就出来了, 我们有100层楼, 则有

x*(x+1)/2 >=100, 解出来 x >= 14

  • 2.动态规划穷举法

    • 现在考虑动态规划解法, 根据上面的分析,其实可以发现, 第一次尝试的楼层, 会对最终的结果产生直接的影响。 现在 设 f(m, n) 为m个鸡蛋, n层楼时, 在最坏情况下的最少尝试次数。 如果第一次尝试从x层楼开始, 则$ f( m,n ) = 1+ max ( f(m-1, x-1) , f(m, n-x))$
    • $f(m-1, x-1) $对应的是第一次尝试鸡蛋被摔碎的情况
    • $f(m, n-x) $对应的是 第一次尝试鸡蛋没被摔碎的情况

    接下来,只需要编写动态规划程序, 将每一种x都尝试一次, 计算出最小的结果即可

这里给出Java代码


public class EggDropPuzzle {
	public static void main(String[] args) {
		int floors = 100;
		int eggs = 2;
		
		System.out.println(computeMinDropsInWorstCase(eggs, floors));
	}
	
	// A utility function to get maximum of two integers
    static int max(int a, int b) { return (a > b)? a: b; }

	private static int computeMinDropsInWorstCase(int eggs, int floors) {
		int table[][]=new int[eggs+1][floors+1];
		
		
		// boundary condition:
		// if no floors or 1 floors, only need 0 trails or 1 trails
		
		for (int i = 0; i <= eggs; i++) {
			table[i][1] = 1;
			table[i][0] = 0;
		}
		
		
		// if only one egg,   f(1,k) = k
		
		for (int j = 0; j <= floors; j++) {
			table[1][j] = j;
		}
		
		// for the rest of cases
		// f( eggs, floors) = 1+ Max(f( eggs-1 , floors-1), f( eggs, floors-x))
				// x is the floor number we choose to drop for current attempt 
				// range of i = 1,2,.....,floors,
		for(int i = 2; i <= eggs; i++)
		{
			for (int j = 2; j <= floors; j++) {
				
				table[i][j] = Integer.MAX_VALUE;  // so important
				
				for (int floorTriedFirst = 1; floorTriedFirst <= j; floorTriedFirst++) {
					int res = 1+max(table[i-1][floorTriedFirst-1], 
									table[i][j-floorTriedFirst]);  
					if(res < table[i][j])
					{
						table[i][j] = res;
					}
				}
				
			}
		}
		
		return table[eggs][floors];
	}

}

展开阅读全文

没有更多推荐了,返回首页