單連結串列和之戀和海枯石爛題之分析

謝工在GitChat發表於2013-07-02

兩個單連結串列(singly linked list),每一個節點裡面一個0-9的數字,輸入就相當於兩個大數了。然後返回這兩個數的和(一個新list)。這兩個輸入的list長度相等。

要求是:

  1. 不用遞迴;
  2. 要求演算法在最好的情況下,只遍歷兩個list一次 ,最差的情況下兩遍。

關於海枯石爛的分析:

原題:兩個玩家,一堆石頭,假設多於100塊,兩人依次拿,最後拿光者贏,規則是:

  1. 第一個人不能一次拿光所有的;
  2. 第一次拿了之後, 每人每次最多隻能拿對方前一次拿的數目的兩倍。求先拿者必勝策略, 如果有的話。怎麼證明必勝。

分析:
這是斐波那契博弈,當且僅當石頭個數是斐波那契數的時候先手必敗。

讓我們用第二數學歸納法證明:

為了方便,我們將 n 記為 f[i]

1、當 i=2 時,因為不能全部去完,先手只能取1顆,顯然必敗,結論成立。
2、假設當 i<=k 時,結論成立。

則當 i=k+1 時,f[i] = f[k]+f[k-1]

則我們可以把這一堆石子看成兩堆,簡稱 k 堆和 k-1 堆。

(一定可以看成兩堆,因為假如先手第一次取的石子數大於或等於f[k-1],則後手可以直接取完 f[k] ,因為 f[k] < 2*f[k-1]

對於 k-1 堆,由假設可知,不論先手怎樣取,後手總能取到最後一顆。下面我們分析一下後手最後取的石子數 x 的情況。

如果先手第一次取的石子數 y>=f[k-1]/3 ,則這小堆所剩的石子數小於 2y ,即後手可以直接取完,此時 x=f[k-1]-y ,則 x<=2/3*f[k-1]

我們來比較一下 2/3*f[k-1]1/2*f[k] 的大小。即 4*f[k-1]3*f[k] 的大小,對兩值作差後不難得出,後者大。

所以我們得到,x<1/2*f[k]

即後手取完 k-1 堆後,先手不能一下取完 k 堆,所以遊戲規則沒有改變,則由假設可知,對於 k 堆,後手仍能取到最後一顆,所以後手必勝。

i=k+1 時,結論依然成立。

那麼,當 n 不是Fibonacci數的時候,情況又是怎樣的呢?

這裡需要藉助“Zeckendorf定理”(齊肯多夫定理):任何正整數可以表示為若干個不連續的Fibonacci數之和。

關於這個定理的證明,感興趣的同學可以在網上搜尋相關資料,這裡不再詳述。

分解的時候,要取儘量大的Fibonacci數。

比如分解85:85在55和89之間,於是可以寫成85=55+30,然後繼續分解30,30在21和34之間,所以可以寫成30=21+9,

依此類推,最後分解成85=55+21+8+1。

則我們可以把 n 寫成  n = f[a1]+f[a2]+……+f[ap] 。(a1>a2>……>ap

我們令先手先取完 f[ap] ,即最小的這一堆。由於各個f之間不連續,則 a(p-1) > ap  + 1 ,則有 f[a(p-1)] > 2*f[ap]。即後手只能取 f[a(p-1)] 這一堆,且不能一次取完。

此時後手相當於面臨這個子游戲(只有 f[a(p-1)]這一堆石子,且後手先取)的必敗態,即先手一定可以取到這一堆的最後一顆石子。

同理可知,對於以後的每一堆,先手都可以取到這一堆的最後一顆石子,從而獲得遊戲的勝利。

參考博文:斐波那契博弈

本文來自微信:待字閨中,6月29日釋出,原創@陳利人 ,歡迎大家繼續關注微信公眾賬號“待字閨中”。

相關文章