1、題目:
有一棟樓共100層,一個雞蛋從第N層及以上的樓層落下來會摔破, 在第N層如下的樓層落下不會摔破。給你2個雞蛋,設計方案找出N,而且保證在最壞狀況下, 最小化雞蛋下落的次數。面試
解答2:得5分的答案數組
若是咱們動一下腦子仔細思考這個問題,咱們會獲得一個相對不錯的答案。參加BAT面試那位朋友就給出了下面的這種方案,並自認爲是一種很完美的答案。但面試官給出的回答是:我仍是不滿意。測試
聽說,他這種思路的靈感來自於數學中的求極值問題。優化
已知兩個天然數的和爲25,求這兩個數的平方和的最大、最小值。spa
解:設一個天然數爲x 另外一個天然數爲25-x設計
x²+(25-x)²code
=2x²-50x+625數學
=2(x²-25x+312.5)class
=2[(x-12.5)²-156.25+312.5]原理
=2[(x-12.5)²+156.25]
因此可得:
當x取12.5時 有最小值2×156.25=312.5 (當x==y==12.5時取得極小值)
當x取25時 有最大值2×(12.5²+156.5)=625
所以,很容易獲得啓發(固然,這只是一種直覺,並無什麼理論依據。)。100層樓,平均分紅10分,每份恰好10層。
那麼咱們的作法以下:
將100層樓分紅10分,每一份就是10層樓。首先,將雞蛋從第10層樓開始扔。那麼結果有兩種可能:
狀況1:若是碎了,說明臨界樓層在1到10之間,但如今只剩下一個雞蛋了,只能從第一層一直到第10層。
狀況2:若是沒有碎,接下來從第20層扔雞蛋。
該方法的思路是,用一個雞蛋來試探,找到臨界樓層的大體範圍[1~10]、[11-20]….[91-100]。而後用另外一個雞蛋在大體範圍內找出精確樓層。該方法的最壞次數是:18次
2、思路:
先假設,最小的次數爲x次。
首先在x層摔,那麼會出現兩個結果:
一、碎了,爲了找出那一層碎了,第二個雞蛋必須從1~x-1進行遍歷的摔
二、沒碎,那麼第二次就在x+(x-1)樓層摔。
爲何是x+x-1樓層呢?
首先咱們已經假設了經過x步咱們就能獲得答案,如今咱們在x層已經用了一次了,那麼就只剩下x-1步了。因此咱們選擇x+(x-1)層,若是碎了,咱們就能經過x-2步,遍歷x+1~x+(x-1)-1的全部樓層。
三、若是在x+(x-1)樓碎了,那麼同1,遍歷x+1~x+(x-1)-1
四、沒碎,那麼同2,就在x+(x-1)+(x-2)層摔
…
最後咱們將會得出這樣一個樓層公式x+(x-1)+(x-2)+…+1 = x(x+1)/2。
這個公式有什麼意義呢?
有, x(x+1)/2 >= 100,這樣才能順利的解除x。
有人說,x(x+1)/2 = 99就能夠,若是雞蛋在99層都沒碎,那麼一定是100層。 我想說誰告訴你記得必定會碎!
那麼咱們就順利的解除 x=14。
3、擴展
此題還有一個擴展,就是爲N個雞蛋從M層摔找出最小值。
那就不是很好手解了,因此寫了代碼,使用動態規劃原理。動態規劃式子以下:
f[n][m] = 1+max(f[n-1][k-1],f[n][m-k]) k屬於[1,m-1]
解釋下原理:
一、當手裏有n個的時候,雞蛋從k層往下摔,若是破了,那麼手裏只有n-1雞蛋了,那麼就須要測試f[n-1][k-1]樓層。或者更通俗好理解點的,我 們運用2個雞蛋100樓層的題目舉例子。以上式子變爲:f[2][m] = 1+max(f[1][k-1],f[2][m-k])
那麼當手裏有2個雞蛋的時候,在k層摔,碎了。那麼如今手裏也就只有一個雞蛋了,此時咱們必須遍歷1~k-1找出第一次碎的樓層。因此爲1+f[1][m-k],前面的1表明在k層的操做。
二、沒破,那麼手裏還有n個雞蛋,那麼須要測試k+1~m這些樓層。此時我想問下,當手裏有2個雞蛋測試1~m-k層和手裏有2個雞蛋測試k+1~m有什麼區別?
有人說有,由於樓層越高越容易碎,那實際上是你我的的想法罷了。其實並無區別,因此第一個公式能夠寫爲f[n][m-k]。
最後附上代碼,爲了理解方便,而沒必要從數組從0開始而困擾,這裏就空間多開了點,因此若是拿去用的話,能夠優化下:
package cglib;
public class DeleteNode {
public int countMinSetp(int egg,int num){
if(egg < 1 || num < 1) return 0;
int[][] f = new int[egg+1][num+1];//表明egg個雞蛋,從num樓層冷下來所需的最小的次數
for(int i=1;i<=egg; i++){
for(int j=1; j<=num; j++)
f[i][j] = j;//初始化,最壞的步數
}
for(int n=2; n<=egg; n++){
for(int m=1; m<=num; m++){
for(int k=1; k<m; k++){
//這裏的DP的遞推公式爲f[n][m] = 1+max(f[n-1][k-1],f[n][m-k]) k屬於[1,m-1]
//從1-m層中隨機抽出來一層k
//若是第一個雞蛋在k層碎了,那麼咱們將測試1~k-1層,就能夠找出來,也即1+f[1][k-1],1便是已經扔了一次
//若是第一個雞蛋在k層沒有碎,那麼咱們將測試k+1~m也即m-k層,
// 這裏也是重點!!!!
// 如今咱們手裏有2個雞蛋,要測試m-k層,那麼我想問,此時和你手裏有2個雞蛋要測試1~m-k層有什麼區別?
// 沒有區別!是的在已知k層不碎的狀況下,測試k+1~m層的方法和測試1~m-k沒區別,因此能夠寫成 1+f[n][m-k] 其中1表示爲 在k層的那一次測試
f[n][m] = Math.min(f[n][m],1+Math.max(f[n-1][k-1],f[n][m-k]));
}
}
}
return f[egg][num];
}
public static void main(String[] args) {
DeleteNode e = new DeleteNode();
System.out.println(e.countMinSetp(5,100));
}
}
輸出:14
-------------------------------------------------------------------------------------------------------------------------
有一棟樓共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次便可得出答案。