Coursera Algorithms week1 练习测验:Egg drop 扔鸡蛋问题

转自 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);
    }
}

点赞