【刷題】麪筋-兩顆雞蛋測臨界樓層的問題

題目

  • 有一棟樓,共100層。html

  • 定義:雞蛋在第n層樓扔下,不會碎,第n+1層扔下,會碎,那麼第n層就叫臨界樓層(即最高的安全樓層)面試

  • 你手中有兩個雞蛋(默認理想狀態:兩個雞蛋徹底相同),如何優化嘗試策略,使得使用最少次數,測出臨界樓層安全

  • 即,使用此策略,最差也能夠在多少次之內測出臨界樓層測試

  • (ps:假定雞蛋必定會在某層樓下落後碎掉)優化

思路1:暴力法遍歷

  • 爲了避免讓雞蛋碎掉,咱們從一樓開始測試,這樣只須要一個雞蛋,當到達臨界樓層的上一層n+1時,雞蛋碎了,而後咱們能夠得出n層是臨界樓層,這是最原始的方法,遍歷。.net

  • 這種策略最糟糕的狀況會是測試到100樓雞蛋纔會碎,測試次數是100次,臨界樓層是99樓。設計

  • 最好的狀況是測試到2樓雞蛋就碎了,測試次數是2次,臨界樓層是1層。code

思路2:二分查找

  • 咱們手中有倆雞蛋,爲了充分利用條件,咱們能夠利用第一個雞蛋來縮小範圍。htm

  • 先跑到50樓去扔,沒碎的話,再去75樓去扔···直到第一個雞蛋碎掉。blog

  • 若是咱們從50樓扔,沒碎,說明50樓如下是安全的,50樓以上還有50樓,那咱們再去上面的50樓的一半——75樓去扔,在75樓碎了,說明臨界樓層在50層~74層之間,咱們就利用第二個雞蛋,遍歷51層到74層。這是運用二分法。

  • 這種策略最糟糕的狀況會是50層雞蛋就碎了,49層是臨界樓層,測試次數是1+49次。

  • 最好的狀況是1樓是臨界樓層,測試次數是1+2次。

思路3:平均分組

  • 嘗試使每一個雞蛋的測試任務大體至關,即給100開個平方根,第一個雞蛋只測試整十樓層,第二個雞蛋測試兩個整十樓層之間的樓層。咱們能夠先測10樓,20樓,30樓···,直到第一個雞蛋碎掉。

  • 若是咱們測到30樓,第一個雞蛋碎了,那咱們就用第二個雞蛋遍歷測試21~29樓。

  • 這種策略最糟糕的狀況會是99層是臨界樓層,測試次數是10+9次。

  • 最好的狀況是1樓是臨界樓層,測試次數是1+2次。

思路4:非平均分組

  • 咱們假定存在一種最優策略,最多n次測試就能找到臨界樓層。那麼,最糟糕的情況會是哪種呢?

  • 從上面的分析能夠看出,測試次數分爲兩部分

    • 第一顆雞蛋的測試次數x,用來縮小範圍
    • 第二顆雞蛋用來在小範圍內查找臨界樓層y
    • 即x+y=n
  • 那麼當測試次數固定爲n時,每當x增長1,y則減小1

    • 即,每當第一顆雞蛋測試一次,那麼所留給第二顆雞蛋探查的範圍就應該減小1
  • 例如:

    • 若是n=10,那麼第一次探查了20樓,使用了一次機會,若是碎了,肯定的範圍是1~19,那麼,第二顆雞蛋須要使用10-1次機會去探查19層,在最糟糕的情形下顯然沒法完成。

    • 顯然當n=10時,第一次探查爲10樓顯然更合適,肯定下來的範圍是1~9,第二顆雞蛋使用10-1次探查9層樓,最糟糕的情形下也能知足。

    • 若是探查10樓後雞蛋沒碎,而在第二次探查時碎了呢?咱們第二次探查應該把範圍再縮小1,若是第一個雞蛋多探查一次,那麼留給第二顆雞蛋的探查機會就少一次,咱們要保證在最糟糕的情形下也能探查到,因此留給第二顆雞蛋的探查範圍應該與其探查機會相等。即第一顆雞蛋的第二次機會應該探查第19層,肯定下來的範圍須要探查的範圍是11~18,第二顆雞蛋的剩餘探查次數恰好爲10-2,匹配成功

    • ·····

    • 根據以上分析,咱們能夠發現,每一次的探查範圍都減一,即n-1,n-2,n-3,....2,1,0

    • 最後咱們的探查範圍會縮小到0

    • 那麼咱們把這些探查範圍加起來,再加上n就是咱們的探查總範圍

  • 即n+(n-1)+(n-2)+······+3+2+1+0=100

    • 咱們先假設最壞狀況下,雞蛋下落次數爲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

    • 注:算式中的項表示的是第一顆雞蛋走的層數,所以是從n開始。

  • 解恰好爲正整數14,即便用此策略最多探查14次便可在100樓中找到臨界樓層

  • 另外,當探查總範圍發生改變時,解的n可能爲小數,顯然,探查次數只能爲整數且n越小探查總範圍越小,

  • 即n應向上取整

  • 我先用第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次便可得出答案。

代碼實現

  • 這個問題還有一個泛化的版本,即d層樓,e個雞蛋,而後設計方案找出N, 使最壞狀況下測試的次數最少。這個要用動態規劃(DP)來解。

  • f[d][e]表示d 層樓,e個雞蛋時,最壞狀況下的測試次數,則:

  • f[d][e]=min{max(f[d-i][e]+1,f[i-1][e-1]+1)},i=1,2,...,d;

  • f[k][1]=k,0<=k<=d,f[0][0...e]=0;

  • 實現代碼以下:

int min_testnumber(int d, int e)  
{  
        int **f=new int *[d+1];  
        int i,j,k;  
        for(i=0;i<=d;i++)  
            f[i]=new int[e+1];  
        for(i=0;i<=d;i++)  
            f[i][1]=i;  
        for(i=0;i<=e;i++)  
            f[0][e]=0;  
        for(i=1;i<=e;i++)  
        {  
            for(j=1;j<=d;j++)  
            {  
                int tmp;  
                int min_test=0x7FFFFFFF;  
                for(k=1;k<=j;k++)  
                {  
                    tmp=f[j-k][i]+1>f[k-1][i-1]+1?f[j-k][i]+1:f[k-1][i-1]+1;  
                    if(tmp>min_test)  
                        min_test=tmp;  
                }  
                f[j][i]=min_test;  
            }  
       }  
       return f[d][e];  
}

擴展

  • 若是咱們有三個雞蛋,有k次機會,咱們最大能夠測試多少層樓?

    • 思路同前面同樣,第一次測試,不能過高也能過矮,必須恰到好處,也就是第一枚雞蛋若是破碎,剩餘k-1次機會能將剩餘樓層給測試完。

    • 由上面結論,k-1次機會最多能夠測試k(k-1)/2層樓,因此第一次在k(k-1)/2+1層樓,第一次若是第一枚雞蛋不碎,第二次在此基礎上增長(k-1)(k-2)/2+1層樓,因而,三個雞蛋k次機會總共測試樓層數爲

    • k=9.

  • 至於四個雞蛋,五個雞蛋,以致於M個雞蛋,能夠以此類推,方法同上。

參考連接

END

相關文章
相關標籤/搜索