解谷歌的一道題

2021-05-22 19:40:36 字數 2486 閱讀 5247

這幾天有一道google的面試題在論壇炒得很火,題目如下:「有乙個100層高的大廈,你手中有兩個相同的玻璃圍棋子。從這個大廈的某一層扔下圍棋子就會碎,用你手中的這兩個玻璃圍棋子,找出乙個最優的策略,來得知那個臨界層面。」下面給出我的分析和解答。

為了得到兩個棋子的最優策略,我們先簡化問題,看看乙個棋子的情況。如果手中只有乙個棋子,為了得知臨界層面,你只有一種選擇:從2樓開始,一層一層地試,直到棋子被打碎,此時你站的樓層就是所求的臨界層面。在最差的情況下,我們需要投擲99-2+1=98次,你可能奇怪為什麼不是100-2+1=99次,那是因為題目已經告訴我們「從這個大廈的某一層扔下圍棋子就會碎」,所以在99層扔下來還沒碎的話就不用去100層了——從那裡扔它一定會碎。

從乙個棋子的策略我們可以看出,乙個棋子就足以解答這個問題了。現在又多了乙個棋子,該如何利用它呢?很自然地,我們希望能通過這個棋子縮小這種一層一層查詢的範圍。為了縮小範圍,我們將整個大廈的層數分成x段,在這x段中查詢那個臨界段,然後在臨界段中再一層一層地找臨界層。比如可以將大樓分成4段,我們分別在25層、50層、75層投擲棋子,以確定臨界段;如果臨界段在25層到50層,我們再從26層開始一層一層查詢臨界層。

分析到這裡,問題就轉化成了如何確定分段數x使棋子投擲的次數最少的問題。在最差的情況下,要確定臨界段,我們需要投擲100/x-1次;確定了臨界段之後要確定臨界層,我們需要再投擲x-1次。因此,問題就成了求函式f(x)=(100/x-1)+(x-1)的最小值問題。先對f(x)求導,f』(x)=1-100/x2,令f』(x)=0求出駐點x=10(x=-10捨去)。由於f(x)存在最小值且只有乙個駐點,所以當x=10時f(x)取得最小值,最小值為18。這樣就解答了這個問題。

其實10這個結果也很容易直接看出來。在只有乙個棋子時,我們相當於把整個大廈分成了一段,這一段有100層。在有兩個棋子時,我們有很多分法,但無論怎麼分,如果分成k1段,每段有k2層,那麼就有k1k2=100。在最壞的情況下,我們需要投擲(k1-1)+(k2-1)次。因此問題也可以表述成在k1k2=100的條件約束下,如何讓函式f(k1,k2)= k1+k2最小。在初等數學中,我們知道在矩形面積一定的情況下,正方形的周長最小。利用這個結論,我們可以直接得出結論k1=k2=10。

現在問題已經完滿解決,但我還想把這個問題擴充套件一下,把它變成「m層樓n個棋子」的情況。首先來看這樣乙個問題,給定m層樓,多少個棋子就「足夠」了,也就是說,再多的棋子也不能加快查詢的過程。在我所能想到的方法裡,二分法應該是最優的,如果按二分法來查詢,則需要ceiling(log2m)個棋子(ceiling是向上取整函式),超過這個數再多的棋子也無益。

如果n>=ceiling(log2m),那就採用二分法,現在考慮n< ceiling(log2m)的情況。前面已經看到,當n=2時,問題可以表述成在k1k2=100的條件約束下,求函式f(k1,k2)= k1+k2的最小值。類似地,在n個棋子的情況下,問題可以表述成在k1k2…kn=m的條件約束下,求函式f(k1,k2,…,kn)=k1+k2+…+kn的最小值。利用拉格朗日乘數法,我們可以很容易地求出:當k1=k2=…=kn=n√m時,這個多元函式取得最值。n√m有可能不是整數,因此這只是乙個理論上的結果。

我們換乙個思路考慮,m層樓n個棋子的問題其實就是如何將m分解成n個因子相乘,從而讓各個因子之和最小。如何分解m使得策略最優就成了問題的關鍵。前面得出的結論提示我們盡量讓各個因子相等或者相差較小,它們相加的結果才會較小。比如,100層樓3個棋子的情況,5,5,4應該是乙個最優的選擇。

考慮到這裡,又有乙個問題出現了:是不是將m分解的越多越好呢?比如,將100分解成10,10好呢,還是2,5,10好?這個問題其實就是在問,兩個大於1的整數,它們的和大呢還是積大。很明顯,當然是積大,因此將m分解的越多越好。

數論告訴我們,質數是整數的基礎,所有整數都可以分解成若干個質數的乘積。因此,如果將上面的方法發揮到極致,那就要求我們把m分解成質數的乘積。當然,如果棋子足夠多,這並不是最優的方法,對質數層樓的段,你仍然可以採用二分法。

上文貼出之後,我又在csdn和chinaunix的論壇看了一些網友的解法,發現上述方法並非最優。將大樓分段以縮小查詢範圍的想法是沒錯的,問題在於是否應該均勻分段。

題目要求我們總的投擲次數要最少。在分段之後,總的投擲次數就等於確定臨近段的次數加上確定臨界層的次數。如果我們均勻分段,則確定臨界層的最壞投擲數是固定的(9次),隨著我們確定臨近段的投擲次數增加,總的投擲次數也在增加。這樣一來,隨著臨界段的不同,投擲次數也不同。

這也就是為什麼上述方法不是最優的原因:投擲次數分布不均。按最壞情況估計,這種方法就多做了幾次。為了使最壞情況的投擲數最小,我們希望無論臨界段在**,總的投擲數都不變,也就是說將投擲數均勻分布。

接下來的解決方案就很容易想出了:既然第一步(確定臨界段)的投擲數增加不可避免,我們就讓第二步(確定臨界層)的投擲數隨著第一步的次數增加而減少。第一步的投擲數是一次一次增加的,那就讓第二步的投擲數一次一次減少。假設第一次投擲的層數是f,轉化成數學模型,就是要求f+(f-1)+...+2+1>=99,即f(f+1)/2>=99,解出結果等於14。

這種方法要推廣到n(n>2)個棋子的情況比較困難。我初步的想法是,先用均勻分段求出乙個解,然後修正這個解使投擲次數均勻分布。如果你對此有興趣,不妨思考一下具體的解法。

解一道遞迴呼叫題

int ack int m,int n ack 3,3 的輸出是什麼?假設我們將ack m,n 填充到乙個二維矩陣的第m行第n列,從函式中可以看出第m行第n列的資料依賴於第m 1行和第m行第n 1列的資料。根據if m 0 return n 1 可以填充第0行的資料為0,1,2,3,4,5,6,7....

解一道簡單的演算法題

原題是這樣的 乙個未排序整數陣列,有正負數,重新排列使負數排在正數前面,並且要求不改變原來的正負數之間相對順序。比如 input 1,7,5,9,12,15 ans 5,12,1,7,9,15 且要求時間複雜度o n 空間o 1 我實現的解法 解法1 不考慮時間和空間複雜度 則每次找到乙個正數,取出...

巧解一道階乘排序題

今天,介紹一道在本計算機考試書上的階乘排序 就是將乙個列表長度的階乘種的方式的排列組合輸出 題。下面我們直接看題 在1 49中任意選擇7個數,將這7個數的所有排列可能性全部輸出。例如 1,2,3 的所有輸出有 2,1,3 2,3,1 3,2,1 3,1,2 1,3,2 1,2,3 這道題的關鍵是要找...