1300:雞蛋的硬度
時間限制: 1000 ms 內存限制: 65536 KB
提交數: 1055 通過數: 705
【題目描述】
最近XX公司舉辦了一個奇怪的比賽:雞蛋硬度之王爭霸賽。參賽者是來自世界各地的母雞,比賽的內容是看誰下的蛋最硬,更奇怪的是XX公司並不使用什么精密儀器來測量蛋的硬度,他們采用了一種最老土的辦法--從高度扔雞蛋--來測試雞蛋的硬度,如果一次母雞下的蛋從高樓的第a層摔下來沒摔破,但是從a+1層摔下來時摔破了,那么就說這只母雞的雞蛋的硬度是a。你當然可以找出各種理由說明這種方法不科學,比如同一只母雞下的蛋硬度可能不一樣等等,但是這不影響XX公司的爭霸賽,因為他們只是為了吸引大家的眼球,一個個雞蛋從100 層的高樓上掉下來的時候,這情景還是能吸引很多人駐足觀看的,當然,XX公司也絕不會忘記在高樓上掛一條幅,寫上“XX公司”的字樣--這比賽不過是XX 公司的一個另類廣告而已。
勤於思考的小A總是能從一件事情中發現一個數學問題,這件事也不例外。“假如有很多同樣硬度的雞蛋,那么我可以用二分的辦法用最少的次數測出雞蛋的硬度”,小A對自己的這個結論感到很滿意,不過很快麻煩來了,“但是,假如我的雞蛋不夠用呢,比如我只有1個雞蛋,那么我就不得不從第1層樓開始一層一層的扔,最壞情況下我要扔100次。如果有2個雞蛋,那么就從2層樓開始的地方扔……等等,不對,好像應該從1/3的地方開始扔才對,嗯,好像也不一定啊……3個雞蛋怎么辦,4個,5個,更多呢……”,和往常一樣,小A又陷入了一個思維僵局,與其說他是勤於思考,不如說他是喜歡自找麻煩。
好吧,既然麻煩來了,就得有人去解決,小A的麻煩就靠你來解決了:)
【輸入】
輸入包括多組數據,每組數據一行,包含兩個正整數n和m(1≤n≤100,1≤m≤10),其中n表示樓的高度,m表示你現在擁有的雞蛋個數,這些雞蛋硬度相同(即它們從同樣高的地方掉下來要么都摔碎要么都不碎),並且小於等於n。你可以假定硬度為x的雞蛋從高度小於等於x的地方摔無論如何都不會碎(沒摔碎的雞蛋可以繼續使用),而只要從比x高的地方扔必然會碎。
對每組輸入數據,你可以假定雞蛋的硬度在0至n之間,即在n+1層扔雞蛋一定會碎。
【輸出】
對於每一組輸入,輸出一個整數,表示使用最優策略在最壞情況下所需要的扔雞蛋次數。
【輸入樣例】
100 1 100 2
【輸出樣例】
100 14
【分析】
這個題目有很多地方都有分析,我也看了好幾個解答,但都說得不太明白(也許是我笨),我結合聽的講座和各家分析,加上自己的試驗作以下分析。
這時的策略大體如下:第一,最簡單的和種情況就是只有一只雞蛋,那只能從一樓開始一層一層往上走,如果第一樓就破了,那硬度就是0,否則總能找到破與不破的兩層。那意味着硬度為多少就得比硬度數多扔一次就好。如果題目給的樓層是100,那最壞的可能就是硬度為99,必須扔100次。(硬度不能為100,題目說了硬度小於n,也意味着從頂樓扔下,雞蛋一定會碎,但這一次必須要扔)。第二,如果有兩枚雞蛋呢?那第一次就不用在第一樓扔,那在多少樓扔合適?答案是第14樓。那要是破了呢?說明硬度最大13,同時我們手里的雞蛋也只有一枚了,那辦法就只有一個了:從第一樓開始往上,根據剛才的經驗,最多再扔13次就好(1-13樓全試,只有一枚雞蛋,不能冒險喲)。那要是沒破呢?又在哪扔呢?第27樓。要是破了,硬度在14-26之間,最多再試12次就可以了,加上之前14樓和27樓兩次,共14次。要是沒破,下一次選在39樓。同樣要是破了,就從28開始往上走,最多走到第38樓,共14次,要是沒破,就選50樓。。。。,以后每多一次間距就少一,均能保證14次能測出硬度。第三,為什么是14樓開始?10樓不行么?如果選10樓,要是破了,當然比剛才更快,那要是沒破呢?下一次選多少樓?20?然后30、40、50、60、70、80、90?到了60己經扔了6次了,要是破了,最壞的情況是要再扔9次,在是沒破又如何繼續?明顯不是最佳策略。那為什么是14次?按照剛才的策略1+2+3+...+14=105超過100了,說明14次一定能測出結果。第四,要是雞蛋有更多個呢?比如有三個,第一次可以考慮對分:第50樓,要是不破,再二分,75樓.....,要是破了,就相當於兩個雞蛋測50樓,1+2+3+...+10=55,再有10次就可以搞定了。如果有四個雞蛋呢?兩次就把范圍縮小到25樓了,1+2+3+...+7=28,共需9次。那要是雞蛋無限呢,最多也是全二分,2^7=128,這個就是最效率的了,換句話說,對100層樓而言,多於7個雞蛋也沒用。第五,如果有m個雞蛋n層樓呢?我們用f[n][m]記最少的次數。那可以枚舉從第k層樓扔下,有兩種情況:(1)雞蛋破了,那硬度在1-k-1之間,我們得用余下的m-1個雞蛋測試k-1層樓,這時f[n][m]=f[k-1][m-1],(2)雞蛋沒破。那再測余下的k~n層樓就好,這時f[n][m]=f[n-k][m],題目要求是最壞的情況,故需要取這兩種情況的最大值。故f[n][m]=max(f[k-1][m-1],f[n-k][m])+1。對所有的k的取值得到若干個f[n][m],這個我們可以選擇k值,故把這些值再取最小值。f[n][m]=min(f[n][m],max(f[k-1][m-1],f[n-k][m])+1),這就是帥氣的狀態轉移方程了。

//1300:雞蛋的硬度 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int f[110][11]; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) f[i][j]=i; for(int j=1;j<=n;j++) for(int i=2;i<=m;i++) for(int k=1;k<=j;k++)//枚舉 f[j][i]=min(f[j][i],max(f[k-1][i-1],f[j-k][i])+1); /*在1~j層樓間選擇第k層扔下雞蛋,如果破了,說明硬度介於1~k-1之間 , 問題變成用i-1個雞蛋去測k-1層樓的硬度。如果沒破則說明硬度介於k~j之間, 問題變成用i個雞蛋去測j-k層樓的硬度,題目要求是最壞的情況下,故要取兩者最大值 */ printf("%d\n",f[n][m]); } return 0; }