2個雞蛋從100層摔(N個雞蛋從M樓層摔)

一、題目:

有一棟樓共100層,一個雞蛋從第N層及以上的樓層落下來會摔破, 在第N層以下的樓層落下不會摔破。給你2個雞蛋,設計方案找出N,並且保證在最壞情況下, 最小化雞蛋下落的次數。(假設每次摔落時,如果沒有摔碎,則不會給雞蛋帶來損耗)

二、解題思路

我們先假設最壞情況下,雞蛋下落次數爲x,即我們爲了找出N,一共用雞蛋做了x次的實驗。 那麼,我們第一次應該在哪層樓往下扔雞蛋呢?先讓我們假設第一次是在第y層樓扔的雞蛋, 如果第一個雞蛋在第一次扔就碎了,我們就只剩下一個雞蛋,要用它準確地找出N, 只能從第一層向上,一層一層的往上測試,直到它摔壞爲止,答案就出來了。 由於第一個雞蛋在第y層就摔破了, 所以最壞的情況是第二個雞蛋要把第1到第y-1層的樓都測試一遍,最後得出結果, 噢,原來雞蛋在第y-1層才能摔破(或是在第y-1層仍沒摔破,答案就是第y層。) 這樣一來測試次數是1+(y-1)=x,即第一次測試要在第x層。OK, 那如果第一次測試雞蛋沒摔破呢,那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

得出答案是14。
即我先用第1個雞蛋在以下序列表示的樓層數不斷地向上測試,直到它摔破。 再用第2個雞蛋從上一個沒摔破的序列數的下一層開始,向上測試, 即可保證在最壞情況下也只需要測試14次,就能用2個雞蛋找出從哪一層開始, 往下扔雞蛋,雞蛋就會摔破。

14, 27, 39, 50, 60, 69, 77, 84, 90, 95, 99, 100

比如,我第1個雞蛋是在第77層摔破的,那麼我第2個雞蛋就從第70層開始,向上測試, 第二個雞蛋最多只需要測試7次(70,71,72,73,74,75,76),加上第1個雞蛋測試的 7次(14,27,39,50,60,69,77),最壞情況只需要測試14次即可得出答案。

三、擴展
此題還有一個擴展,就是爲N個雞蛋從M層摔找出最小值。
那就不是很好手解了,所以寫了代碼,使用動態規劃原理。動態規劃式子如下:

counts[n][m]=1+Math.max(counts[n-1][k-1], counts[n][m-k])

解釋下原理:
1、當手裏有n個的時候,雞蛋從k層往下摔,如果破了,那麼手裏只有n-1雞蛋了,那麼就需要測試counts[n-1][k-1]樓層。或者更通俗好理解點的,我們運用2個雞蛋100樓層的題目舉例子。以上式子變爲:counts[2][m] = 1+max(counts[1][k-1],counts[2][m-k])
  那麼當手裏有2個雞蛋的時候,在k層摔,碎了。那麼現在手裏也就只有一個雞蛋了,此時我們必須遍歷1~k-1找出第一次碎的樓層。所以爲1+counts[1][m-k],前面的1代表在k層的操作。
2、沒破,那麼手裏還有n個雞蛋,那麼需要測試k+1~m這些樓層。

此時我想問下,當手裏有2個雞蛋測試1~m-k層和手裏有2個雞蛋測試k+1~m有什麼區別?
有人說有,因爲樓層越高越容易碎,那其實是你個人的想法罷了。其實並沒有區別,所以第一個公式可以寫爲counts[n][m-k]。

四、動態規劃代碼

public class Solution {
    public static void main(String[] args) {
        Solution s=new Solution();
        System.out.println(s.countMinStep(2, 100));
    }
    public int countMinStep(int eggs,int levels){
        int[][] counts=new int[eggs+1][levels+1];
        for(int i=1;i<=eggs;i++){
            for(int j=1;j<=levels;j++){
                counts[i][j]=j;
            }
        }
        for(int n=2;n<=eggs;n++){
            for(int m=1;m<=levels;m++){
                for(int k=1;k<m;k++){
                    counts[n][m]=Math.min(counts[n][m], 1+Math.max(counts[n-1][k-1], counts[n][m-k]));
                }
            }
        }
        return counts[eggs][levels];
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章