[面試題][總結]100層樓丟玻璃球測試臨界可破層數,用兩個一模一樣的球來測試計算盡可能少的次數?


1 題目描述

   有一棟100層高的大樓,給你兩個完全相同的玻璃球。假設從某一層開始,丟下玻璃球會摔碎。那么怎么利用手中的兩個球,用什么最優策略知道這個臨界的層是第幾層?

2 解法匯總

2.1 遞推方法一

  第一次扔k層 ,則次數time=1,第二次,如果破了,要試從1到k-1層,此時需要Time=time+k-1=k 次;如果沒破,還要扔k層,則次數為time=2;如果破了,還要扔k+1到2k-1層,再加上2 即Time=Time+k-2=k。還是K次;注意每多扔一次 少測試一層。次數卻多一次。實際只要能測到n-1層就夠了。

  以此類推如果滿足 k+(k-1)+(k-2)+(k-3)+(k-4)+....+2+1 >= n-1 。可以化簡得到:k(k-1)>=2(n-1)

這里,n=100 所以 解得k=14。所以只要14次就可以確認那層試臨界層。

2.2 圖形法

首先從題目得出基本思路

1.第一個球應該低到高試,但不是每層必;

2.不能有僥幸心理,第二個球在第一個球的區間里每層必。

    上圖是簡化為10層樓解法。數字代表樓層,球從原點先右后上的路徑對應的方格中的數字進行測試.也就是第一個球測試4\7\9\10層.如果第一個球4層壞了,第二個球測試1\2\3。如果第一個球7層壞了,第二個球測試5\6。依次類推,肯定可以測出最終的層數。這樣做摔4次肯定能得出結果,是最優的方案。

  如果把上圖的每一個層看作一個1*1的正方形。上圖就近似一個等腰梯形,面積為4*4/2+4*0.5=10,也就是層數。推廣開來,對於邊長為N的圖形,它所能測試的層數就是N*N/2+N*0.5。對於M層樓,最優方案只是上圖的類推。將1到M按照上圖類同的方法排布,並按照先右后上的路徑。這肯定是最優解。也就是N*N/2+N*0.5〉=M。這里只要取得N的最小正整數就是最多的嘗試次數。比如100層的情況是: N*N/2+N*0.5>=100。解得N的最小正整數解是14。

  從數學證明的角度來看:最優解是怎樣的呢?問題已經可以轉化為在坐標系的原點出發,只能先右后上的,在相同的面積下,哪一種圖形的使得原點到該圖形的任意一點的距離的最大值最小。結論是:在相同的面積中,直角等腰的三角形到達面上的任意一點的最大距離是最小的。直角等腰三角形的斜邊上任意一點到達原點的距離都是一樣的,也是直角等腰三角形中距離原點最大的。利用反證法,如果還有比直角等腰三角形更好的圖形,必然要挖去斜邊上所有的點,但是把這些點放在哪里呢?放在哪里都比現在的位置遠。

2.3 動態規划

  設f(a, b)為a個球做b次測試可以測試到的樓層數,可以確定的樓層數即為f(a, b) + 1,因為第1層不需測試,需要測試的樓層號僅僅為[2, f(a, b) + 1]共f(a, b)層,也就是a個球b次測試可以測試到的樓層數。考慮第1次測試,測試的樓層記為x:

1)如果球破了,就需要測試x下面的樓層,還剩下a-1個球b-1次測試,測試的樓層數為f(a - 1, b - 1)。

2)如果球沒有破,那么需要測試x上面的樓層,還剩下a個球b-1次測試,測試的樓層數為f(a, b - 1)。

a個球b次測試為1)2)測試的樓層數及第1次測試了的1層,所以:

f(a, b) = f(a - 1, b - 1) + f(a, b - 1) + 1                                              (1)

考慮初始條件,顯然f(a, 1) = 1(a >= 1,1次測試可以測試到的樓層數當然為1,不論多少個球),f(1, b) = b(b >= 1,1個球做了b次測試當然測試到了b層樓)。

強調一下:注意f(a, b)為測試到的樓層數,f(a, b)加上不需測試的樓層才是可以確定的樓層(f(a, b) + 1)。

一般來說,a >= 2(1個球意義不大),可以計算出f(2, 64) = 2080,f(3, 64) = 43744,f(4, 64) = 679120。

  1 /* 
  2  * a balls, n floors, want to find the minimum number of floor 
  3  * where a ball drops will be broken. output the minimum number 
  4  * of drops 
  5  * METHOD: dynamic programming 
  6  * assum the answer is b, that is the number of drops 
  7  * f(a, b): the maximum number of floors, when a balls and b drops 
  8  * f(a, b) = 1 + f(a, b - 1) + f(a - 1, b - 1) 
  9  * obviously, f(a, 1) = 1; f(1, b) = b 
 10  */  
 11 #include <stdio.h>  
 12 #include <stdlib.h>  
 13 #include <assert.h>  
 14 #include <string.h>  
 15 #define DEBUG  
 16 #define MAX_B 64  
 17 #define MAX_A 16  
 18 #define f(a, b) ff[a - 1][b - 1]  
 19 static unsigned int a, n;  
 20 static unsigned long long ff[MAX_A][MAX_B];  
 21 static void init()  
 22 {  
 23     int i;  
 24     memset(ff, 0, sizeof(ff));  
 25     /*f(a, 1) = 1*/  
 26     for (i = 1; i <= MAX_A; i++){  
 27         f(i, 1) = 1;  
 28     }  
 29     /*f(1, b) = b + 1*/  
 30     for (i = 1; i <= MAX_B; i++){  
 31         f(1, i) = i;  
 32     }  
 33 }  
 34 static unsigned long long do_find_min_drops(int i, int j)  
 35 {  
 36     if (f(i, j))  
 37         return f(i, j);  
 38     f(i, j) = do_find_min_drops(i - 1, j - 1) +   
 39         do_find_min_drops(i, j - 1) + 1;  
 40     return f(i, j);  
 41 }  
 42 static void do_print_drops(int i, int j, unsigned long long min,   
 43         unsigned long long max)  
 44 {  
 45     if (min > max)  
 46         return;  
 47     if (1 == i){  
 48         assert(j == max - min + 1);  
 49         for (i = min; i <= max; i++){  
 50             printf("%5d", i);  
 51         }  
 52         printf("/n");  
 53         printf("*************/n");  
 54         return;  
 55     }  
 56     if (1 == j){  
 57         assert(min == max);  
 58         printf("%5lld/n", max);  
 59         printf("*************/n");  
 60         return;  
 61     }  
 62     printf("%5lld", min + f(i - 1, j - 1));  
 63     do_print_drops(i - 1, j - 1, min, min + f(i - 1, j - 1) - 1);  
 64     do_print_drops(i, j - 1, min + f(i - 1, j - 1) + 1, max);  
 65 }  
 66 static void print_drops(int ans)  
 67 {  
 68     do_print_drops(a, ans, 2, n);/*[2..n]*/   
 69 }  
 70 static void find_min_drops()  
 71 {  
 72     /*NOTE: number of floors are [1, n]*/  
 73     int i, j, m;          
 74     int ans;  
 75 #if 0//def DEBUG  
 76     for (i = 2; i <= MAX_A; i++){  
 77         for (j = 2; j <= MAX_B; j++){  
 78             printf("f(%d, %d) = %lld/n", i, j, do_find_min_drops(i, j));  
 79         }  
 80         printf("****************/n");  
 81     }  
 82 #endif  
 83     i = 1;   
 84     j = MAX_B;  
 85     while (i <= j){  
 86         m = (i + j) / 2;  
 87         if (do_find_min_drops(a, m) + 1 < n)  
 88         /* 
 89          * why +1? because the 1st floor need not to test 
 90          */  
 91             i = m + 1;  
 92         else  
 93             j = m - 1;  
 94     }  
 95     ans = i;  
 96     if (ans > MAX_B){  
 97         printf("the number of the maximum drops(MAX_B = %d) is too small/n", MAX_B);  
 98         printf("maximum floors "   
 99                 "can be tested is f(%d, %d) + 1 = %lld + 1. STOP/n", a, MAX_B, f(a, MAX_B));  
100         exit(0);  
101     }  
102     printf("the minimum drops: %d/n", ans);  
103     print_drops(ans);  
104 #ifdef DEBUG  
105     for (i = 1; i <= a; i++){  
106         for (j = 1; j <= ans; j++){  
107             printf("f(%d, %d) = %lld/n", i, j, f(i, j));  
108         }  
109         printf("****************/n");  
110     }  
111 #endif  
112 }  
113 int main(int argc, char **argv)  
114 {  
115     if (3 != argc){  
116         fprintf(stderr, "usage: %s a n/n", argv[0]);  
117         exit(-1);  
118     }  
119       
120     a = atoi(argv[1]);  
121     n = atoi(argv[2]);  
122     printf("a = %d/tn = %d/n", a, n);  
123     assert(a > 0 && a < MAX_A && n > 0);  
124     init();  
125     find_min_drops(); /*drops: 1*/  
126     return 0;  
127 }  
View Code

1)2個球,100層樓時,可以計算出

f(2, 13) = 91
f(2, 14) = 105

  因此需要的測試次數為14。

2)3個球,100層樓,可以計算出
f(3, 8) = 92
f(3, 9) = 129

  因此測試測試最多為9次。可以從38層開始。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM