转自 https://www.cnblogs.com/evasean/p/7208986.html
题目原文:
Suppose that you have an n-story building (with floors 1 through n) and plenty of eggs. An egg breaks if it is dropped from floor T or higher and does not break otherwise. Your goal is to devise a strategy to determine the value of T given the following limitations on the number of eggs and tosses:
Version 0: 1 egg, ≤T tosses.
Version 1: ∼1lgn eggs and ∼1lgn tosses.
Version 2: ∼lgT eggs and ∼2lgT tosses.
Version 3: 2 eggs and ∼2 n \sqrt{n} n tosses.
Version 4: 2 eggs and ≤c T \sqrt{T} T tosses for some fixed constant c
分析:
version0 : 拿着一个鸡蛋从1~n依次扔就可以,到floor T会碎,故复杂度为≤T
version 1: 采用二分查找,首先从n/2层开始扔:
if(鸡蛋碎) 从(n/2)/2层开始扔;
else 从n/2+(n/2)/2层开始扔
二分方法需要lgn个鸡蛋尝试lgn次
version 2: 依次从1, 2, 4, 8, 16, 32,…2k开始扔,如果鸡蛋在2k碎了,那么2k-1≤T≤2k,这时已经使用了 lgT 次步,接下来在[2k-1+1,2k)区间进行version1的二分查找方法,需要花费lgT步。这两种操作加起来总共花费2lgT步
version 3: 将0~n层楼分成[1, n \sqrt{n} n -1], [ n \sqrt{n} n , 2 n \sqrt{n} n -1], [2 n \sqrt{n} n ,3 n \sqrt{n} n -1]…[k n \sqrt{n} n , (k+1) n \sqrt{n} n -1]…个区间,用一个鸡蛋分布从1开始在各个区间的起始楼层扔,如果在k n \sqrt{n} n 层碎了,那就从(k-1) n \sqrt{n} n +1开始逐层扔。第一步区间选择用了 n \sqrt{n} n 的复杂度,第二步区间内部扔鸡蛋用了 n \sqrt{n} n 的复杂度,总共用了2 n \sqrt{n} n
version 4: 尝试从1, 4, 9, 16, 25,…(k-1)2, k2…楼层扔鸡蛋,加入鸡蛋在楼层k2碎了,意味着(k-1)2≤T≤k2,这一步尝试了 T \sqrt{T} T 次(k= T \sqrt{T} T )。接着从楼层(k-1)2+1开始逐层扔,最多尝试至k2-1结束,这一步需要尝试k2-1-(k-1)2-1=2 T \sqrt{T} T -1=2 T \sqrt{T} T -2次。总共用了3 T \sqrt{T} T -2次
代码实现
package eggdrop;
public class EggDrop {
//供预测的提前设定的解,也可以直接random一个,为方便测试直接给定。
public static int T = 10;
//尝试了几次
static int numsOfTry = 0;
//摔碎了多少个鸡蛋
static int numsOfEggBroken = 0;
//扔鸡蛋抽象为一个方法,扔的时候更新尝试次数和鸡蛋碎了没有的次数,同时打印尝试的过程和结果
public static boolean drop(int i){
numsOfTry++;
if(i < T) {
System.out.println("from " + i + " no");
return false;
}
else{
System.out.println("from " + i + " yes");
numsOfEggBroken++;
return true;
}
}
//以下为version0-4的实现,前面注释给出的两个数字分别为
//(摔坏的鸡蛋数的期望,尝试次数的期望)
//1egg;T
public static void go0(int n){
for (int i = 0; i < n; i++) {
boolean tmp = drop(i);
if(tmp == true) {
System.out.println("T is " + i);
break;
}
}
}
//lg(n)egg;lg(n)
//为方便go2调用,把其改为了带上下界的二分的方法。
public static void go1(int n,int start,int end){
boolean tmp = drop((start + end) / 2);;
while(start < end) {
if (tmp == false) {
start = (start + end) / 2 + 1;
tmp = drop((start + end) / 2);
}
else{
end = (start + end) / 2 - 1;
tmp = drop((start + end) / 2);
}
}
if(tmp == false){
System.out.println("T is " + ((start + end) / 2 + 1));
}else{
System.out.println("T is " + (start + end) / 2);
}
}
//lg(T)egg;2lg(T)
public static void go2(int n){
int k = -1;
int start = 0;
int end = n - 1;
for (int i = 0; i < Math.sqrt(n) - 1; i++) {
int t = (int)Math.pow(2,(double)i);
boolean tmp = drop(t);
if(tmp == true){
k = (int)Math.pow(2,(double)i);
start = (int)Math.pow(2,(double)i - 1);
end =(int)Math.pow(2,(double)i);
break;
}
}
go1(n, 0 ,n - 1);
}
//2egg;2根号n
public static void go3(int n){
int l = (int) Math.sqrt(n);
int k = -1;
for (int i = 0; i * l < n; i++) {
boolean tmp = drop(i * l);
if(tmp == true){
k = i;
break;
}
}
for (int i = (k-1) * l + 1; i < k * l; i++) {
boolean tmp = drop(i);
if(tmp == true){
System.out.println("\nT is " + i);
break;
}
}
}
//2egg ;3根号T
public static void go4(int n){
int k = -1;
for (int i = 0; Math.pow(i, 2) < n; i++) {
boolean tmp = drop((int)Math.pow(i, 2));
if(tmp == true){
k = i;
break;
}
}
for (int i = (int)Math.pow(k-1, 2) + 1; i < Math.pow(k, 2); i++) {
boolean tmp = drop(i);
if(tmp == true){
System.out.println("\nT is " + i);
break;
}
}
}
public static void main(String[] args) {
int n = 40;
//方便go2调用
//go1(n,0, n-1);
go4(n);
System.out.println("Number of the Broken Eggs is " + numsOfEggBroken);
System.out.println("Number of trying is " + numsOfTry);
}
}