谷歌“扔鸡蛋问题”

问题:

    假设有2个鸡蛋和100层楼,将鸡蛋从第n层扔下,鸡蛋没有碎,将鸡蛋从n+1层扔下,鸡蛋碎了,那么n层就是鸡蛋不会摔碎的临界点。

问:如何用最少的次数测试出最坏的情况下鸡蛋不会摔破的临界点。

思路一:二分法

    对于均匀列表的查找,我们首先想到的是二分法。在本题中,首先第一个鸡蛋从五十层扔,因为只有两个鸡蛋,假如第一个碎了,第二个不能在使用二分法,所以得从第一层开始一层层实验。那么最坏的情况需要50次。

思路二:开平方

    为了使次数最少,那么两个鸡蛋在最坏的情况下试的次数数应该尽可能接近因为是两个鸡蛋,很容易想到开平方100 = 10 * 10,第一个鸡蛋从10,20···,这样开始扔,那么最坏的情况是10,20,···,90,91,···,99,100共18次

思路三:逆推法

    在思路二中,假设第一个鸡蛋在第十层没有碎,那么问题就变成了2个鸡蛋从90层楼向下扔,求测出临界点所需的最少次数,按照开平方的思路,应该对90开平方,依次类推,可以看出思路二也不是最优解。为了找出最优解,我们采用逆推法:

  • 第一步:假定x是最优解,然后找出第一次鸡蛋应该扔下的楼层
  • 第二步:假设我们从x+1层开始尝试,那么最坏的情况是,鸡蛋x+1层碎了,第二个鸡蛋需要从第一层一直试到x层,共试了x+1次,这与最优解x次矛盾。
  • 第三步:假设我们从x-1层开始尝试,那么最坏的情况是,鸡蛋x-1层碎了,第二个鸡蛋需要从第一层一直试到x-2层,共试了x-1次,这也与最优解x次矛盾。
  • 第四步:从二,三步来看,应该从x层开始扔,假设层鸡蛋碎了,最坏尝试次数为x,和假设相符合。
  • 第五步:假设x层鸡蛋没碎,那么前x层被排除了,那么接下来问题转换成了“从100-x层上扔2个鸡蛋,用最少的次数测试出最坏的情况下鸡蛋不会摔破的临界点”,而该问题的最优解是x-1,且应该从x+(x-1)层,开始测试。
  • 第六步:以第五步类推,假设第一个鸡蛋始终没有碎,最后该开始测试的楼层是x+(x-1)+(x-2)+....+1,因为我们有100层楼,可以得到下面公式:


    谷歌“扔鸡蛋问题”_第1张图片
  • 第七步:因为x是正整数,所以x=14。即最优解是14。
思路四:动态规划

    假设从x层开始测试时,以f(100)表示100层中从x层开始扔鸡蛋测试出临界点所需最少次数,有以下情况:

  • 鸡蛋在x层碎了,最坏还需测试100-x次
  • 鸡蛋在x层未碎,最坏还需要f(x-1)次

    也就是说从x层开始测试最坏的情况是max(100-x,f(x-1))有以下公式:

  • 状态转移方程


  • 边界


    根据上面公式编写java代码

private static int f() {
    int floor = 100;
    int[] temp = new int[floor];
    temp[0] = 1;
    temp[1] = 2;
    for (int i = 2; i < floor; i++) {
        temp[i] = 1 + Math.max(i - 1, temp[0]);
        for (int j = 2; j <= i; j++)
            temp[i] = Math.min(1 + Math.max(i - j, temp[j - 1]), temp[i]);
    }
    return temp[floor - 1];
}

扩展

    假设有n个鸡蛋和m层楼,求是鸡蛋不会摔碎的临界点。同样假设从x层开始测试时,以f(m,n)表示m层中从x层开始扔鸡蛋测试出临界点所需最少次数,有以下情况:

  • 鸡蛋在x层碎了,最坏还需要f(m-x,n-1)次

  • 鸡蛋在x层未碎,最坏还需要f(x-1,n)次
        也就是说从x层开始测试最坏的情况是max(f(m-x,n-1),f(x-1,n)),有以下公式:

  • 状态转移方程


  • 边界


    根据上面公式编写java代码

private static int f(int floor, int eggs) {
    int[][] temp = new int[floor+1][eggs+1];
    for (int i = 1; i <= floor; i++) {
        for (int j = 1; j <= eggs; j++) {
            if (1 == i) {
                //f(1,n) = 1
                temp[i][j] = 1;
            } else if (1 == j) {
                //f(m,1) = m
                temp[i][j] = i;
            } else {
                //f(m,n)=min(1+max(f(m-x,n-1),f(x-1,n)))
                temp[i][j] = 1 + Math.max(temp[i - 2][j - 1], temp[1][j]);
                for (int k = 2; k <= i; k++) {
                    temp[i][j] = Math.min(temp[i][j], 1 + Math.max(temp[i - k][j - 1], temp[k - 1][j]));
                }
            }
        }
    }
    return temp[floor][eggs];
}

你可能感兴趣的:(谷歌“扔鸡蛋问题”)