扔鸡蛋问题

标签: 算法


初始问题:在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。

原文地址:https://www.cnblogs.com/banyu/p/6618996.html