ACM--樓層扔雞蛋問題

雞蛋

Time Limit:1000MS  Memory Limit:65536K

Description

Gardon有一些雞蛋,他現在想知道這些雞蛋的硬度。Gardon的家住在一座很高很高的大樓裏,他現在要在這座大樓上測試雞蛋的硬度。每個雞蛋的硬度相同,雞蛋的硬度定義爲:如果雞蛋從第m層上掉下來沒有破裂,而從第m+1層上掉下來就破裂了,那麼這個雞蛋的硬度就是m。某個雞蛋如果在實驗中破裂了就永遠的損失了。那麼在最壞情況下他最少需要做多少次實驗呢?他的雞蛋數量是有限的。

Input

輸入包括多組數據,每組數據的第一個數是一個整數N,表示Gardon的雞蛋數量,第二個數是大樓的高度H,如果雞蛋的硬度超過H,也按照H計算。

Output

對每組輸入數據,輸出Gardon在最壞的情況下最少的試驗次數,當然必須保證雞蛋的硬度能夠被正確測量出來。

Sample Input

2 100  1 10

Sample Output

14  10
 
類型:動態規劃
有關問題的分析,在網上看到一篇寫的很好的文章
/*以下爲轉載內容*/
/*來自http://49px.com/wiki/%E6%A5%BC%E5%B1%82%E6%89%94%E9%B8%A1%E8%9B%8B%E9%97%AE%E9%A2%98*/
樓層扔雞蛋問題
出自Smilingpoplar

有限層數和蛋數,求即使最壞情況下需要的最少判斷次數
兩個軟硬程度一樣但未知的雞蛋,它們有可能都在一樓就摔碎,也可能從一百層樓摔下來沒事。有座100層的建築,要你用這兩個雞蛋確定哪一層是雞蛋可以安全落下的最高位置。可以摔碎兩個雞蛋。(參見兩個雞蛋--一道Google面試題
這是典型的動態規劃問題。假設f[n]表示從n層樓找到摔雞蛋不碎安全位置的最少判斷次數。假設第一個雞蛋第一次從第i層扔下,如果碎了,就剩一個雞蛋,爲確定下面樓層中的安全位置,必須從第一層挨着試,還需要i-1次;如果不碎的話,上面還有n-i層,剩下兩個雞蛋,還需要f[n-i]次(子問題,實體n層樓的上n-i層需要的最少判斷次數和實體n-i層樓需要的最少判斷次數其實是一樣的)。因此,最壞情況下還需要判斷max(i-1,f[n-i])次。 
 
狀態轉移方程:f[n] = min{ 1+max(i-1,f[n-i]) | i=1..n }   初始條件: f[0]=0(或f[1]=1)
  
實際上,兩個雞蛋的情況用數學方程就可以解決,前提是你知道該怎麼扔: 
一種想法是第一個雞蛋折半搜索,如100層的樓,先從50層扔下去,如果碎了則第二個雞蛋在1~49層樓中自底向上線性搜索;如果沒碎則第一個雞蛋再從75層扔。如果這次碎了則第二個雞蛋在51~74層樓中自底向上線性搜索;如果還沒碎則第一個雞蛋再從88層扔,依此類推。這種方法不是最優,因爲最壞情況下安全位置恰好是49層,需要嘗試50次。 
正確的方法是先假設最少判斷次數爲x,則第一個雞蛋第一次從第x層扔(不管碎沒碎,還有x-1次嘗試機會)。如果碎了,則第二個雞蛋在1~x-1層中線性搜索,最多x-1次;如果沒碎,則第一個雞蛋第二次從x+(x-1)層扔(現在還剩x-2次嘗試機會)。如果這次碎了,則第二個雞蛋在x+1~x+(x-1)-1層中線性搜索,最多x-2次;如果還沒碎第一個雞蛋再從x+(x-1)+(x-2)層扔,依此類推。x次嘗試所能確定的最高樓層數爲x+(x-1)+(x-2)+...+1=x(x+1)/2。 
比如100層的樓,只要讓x(x+1)/2>=100,得x>=14,最少判斷14次。具體地說,100層的樓,第一次從14層開始扔。碎了好說,從第1層開始試。不碎的話還有13次機會,再從14+13=27層開始扔。依此類推,各次嘗試的樓層依次爲 
 
14  27 = 14 + 13  39 = 27 + 12  ...  99 = 95 + 4  100
  
現在推廣成n層樓,m個雞蛋: 
還是動態規劃。假設f[n,m]表示n層樓、m個雞蛋時找到摔雞蛋不碎的最少判斷次數。則一個雞蛋從第i層扔下,如果碎了,還剩m-1個雞蛋,爲確定下面樓層中的安全位置,還需要f[i-1,m-1]次(子問題);不碎的話,上面還有n-i層,還需要f[n-i,m]次(子問題,實體n層樓的上n-i層需要的最少判斷次數和實體n-i層樓需要的最少判斷次數其實是一樣的)。 
 
狀態轉移方程:f[n,m] = min{ 1+max(f[i-1,m-1], f[n-i,m]) | i=1..n }  初始條件:f[i,0]=0(或f[i,1]=i),對所有i  

 

/*以上內容轉自http://49px.com/wiki/%E6%A5%BC%E5%B1%82%E6%89%94%E9%B8%A1%E8%9B%8B%E9%97%AE%E9%A2%98*/

 
下面是我寫的一個程序
Source Code 
#include <iostream>  #include <cstdio>  using namespace std;  #define M 100  #define N 100  int dp[101][101];  int main ()  {      int i,j,k,m,n;      memset(dp,0,sizeof(dp));      for(i=1;i<=N;i++)          dp[1][i]=i;      for(j=2;j<=M;j++)          for(k=1;k<=N;k++)          {                  dp[j][k]=1+max(dp[j-1][0],dp[j][k-1]);                  for(i=2;i<=k;i++)                      dp[j][k]=min(dp[j][k],1+max(dp[j-1][i-1],dp[j][k-i]));          }      while(scanf("%d%d",&m,&n)!=EOF)          printf("%d\n",dp[m][n]);      return 0;  }  

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