扔鸡蛋问题

标签: 算法


初始问题:在100层楼里,给定2个鸡蛋,在安全楼层以上的楼扔鸡蛋会碎。设计一种方案,测试哪层楼是最高安全楼层,要求测试次数最少。

思路:这是典型的动态规划问题。

  假设\(f[n]\)表示从\(n\)层楼找到摔鸡蛋不碎安全楼层的最少判断次数
  假设第一个鸡蛋第一次从第\(i\)层扔下,
  如果碎了,说明安全楼层低于\(i\)。剩一个鸡蛋,为确定\(i-1\)下面楼层中的安全楼层,必须从第一层挨着试直到\(i-1\)层,还需要扔\(i-1\)次鸡蛋;
  如果不碎的话,说明安全楼层高于\(i\)。问题转化为,在以第\(i+1\)层为地面的\(n-i\)层楼里找安全楼层,还有两个鸡蛋,即还需要扔\(f[n-i]\)次鸡蛋。
  因此,在上述两种情况中,最差情况是扔\(1 + max(i-1,f[n-i])\)次鸡蛋。

状态转移方程: $$f[n] = min{ 1+max(i-1,f[n-i]) | i=1..n }$$
初始条件: $$f[0]=0, f[1]=1$$

代码实现

//@num 层数
public static int dropEgg(int num){
	int []dp = new int[num + 1];
	
	dp[0] = 0;
	dp[1] = 1;
		
	for (int n = 2; n <= num; n++) {
		int min = Integer.MAX_VALUE;
		for (int i = 1; i <= n; i++) {
			int max = Math.max(i - 1, dp[n - i]) + 1;
			if(max < min) min = max;
		}
		dp[n] = min;
	}
	return dp[num];
}

问题扩展\(n\)层楼与\(m\)个鸡蛋的问题。

  假设\(f[n,m]\)表示\(n\)层楼、\(m\)个鸡蛋情况下,找到摔鸡蛋不碎的安全楼层的最少判断次数
  假设一个鸡蛋从第\(i\)层扔下,
  如果碎了,还剩\(m-1\)个鸡蛋,为确定下面楼层中的安全位置,还需要扔\(f[i-1,m-1]\)次鸡蛋;
  如果不碎,上面还有\(n-i\)层,还需要扔\(f[n-i,m]\)次鸡蛋。
  因此,在上述两种情况中,最差情况是扔\(1 + max(f[i-1,m-1],f[n-i,m])\)次鸡蛋。

状态转移方程:$$f[n,m] = min{ 1+max(f[i-1,m-1], f[n-i,m]) | i=1..n }$$
初始条件:$$f[i,0]=0,f[i,1]=i$$

//@num 层数
//@egg 鸡蛋数
public static int dropEgg(int num, int egg) {
	int[][] dp = new int[num + 1][egg + 1];
	
	for (int i = 0; i < dp.length; i++) {
		dp[i][0] = 0;
		dp[i][1] = i;
	}

	for (int i = 0; i < dp[0].length; i++) {
		dp[0][i] = 0;
		dp[1][i] = 1;
	}

	for (int n = 2; n <= num; n++) {
		for (int m = 2; m <= egg; m++) {
			int min = num;
			for (int i = 1; i <= n; i++) {
				int max = Math.max(dp[i - 1][m - 1], dp[n - i][m]) + 1;
				if (max < min)
					min = max;
			}
			dp[n][m] = min;
		}
	}
	return dp[num][egg];
}

回到初始问题,怎么设计这个扔鸡蛋方案呢?

  我们先假设最坏情况下,鸡蛋下落次数为\(x\),即我们为了找出\(N\),一共用鸡蛋做了\(x\)次的实验。
  那么,我们第一次应该在哪层楼往下扔鸡蛋呢?
  先让我们假设第一次是在第\(y\)层楼扔的鸡蛋,如果第一个鸡蛋在第一次扔就碎了,我们就只剩下一个鸡蛋,要用它准确地找出\(N\),只能从第一层向上,一层一层的往上测试,直到它摔坏为止,答案就出来了。
  由于第一个鸡蛋在第\(y\)层就摔破了,所以最坏的情况是第二个鸡蛋要把第\(1\)到第\(y-1\)层的楼都测试一遍才得出结果。
  这样一来测试次数是\(1+(y-1)=x\),即第一次测试要在第x层。
  那如果第一次测试鸡蛋没摔破呢,那\(N\)肯定要比\(x\)大,要继续往上找,需要在哪一层扔呢?
  如果第一个鸡蛋在第二次测试中摔破了,那么第二个鸡蛋的测试次数就只剩下\(x-2\)次了(第一个鸡蛋用了2次)。
  这样一来,第二次扔鸡蛋的楼层和第一次扔鸡蛋的楼层之间就隔着\(x-2\)层。我们再回过头来看一看,第一次扔鸡蛋的楼层在第\(x\)层,第1层到第\(x\)层间共\(x\)层;第1次扔鸡蛋的楼层到第2次扔鸡蛋的楼层间共有\(x-1\)层(包含第2次扔鸡蛋的那一层),同理继续往下,我们可以得出,第2次扔鸡蛋的楼层到第3次扔鸡蛋的楼层间共有\(x-2\)层……最后把这些互不包含的区间数加起来,应该大于等于总共的楼层数量100,即

\[x + (x-1) + (x-2) + ... + 1 >= 100 \]

\[(x+1)*x/2 >= 100 \]

得出\(x=14\)

即测试序列为14,27,39,50,60,69,77,84,90,95,99,100。

posted @ 2017-03-25 23:27  斑鱼  阅读(290)  评论(0编辑  收藏  举报