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 }
1)2個球,100層樓時,可以計算出
f(2, 13) = 91 f(2, 14) = 105
因此需要的測試次數為14。
f(3, 8) = 92 f(3, 9) = 129
因此測試測試最多為9次。可以從38層開始。