1. 程式人生 > >catalan數,兩排,分別從低到高排序,第二排相應的比第一排高

catalan數,兩排,分別從低到高排序,第二排相應的比第一排高

一道阿里巴巴的面試題.

題目是這樣的:12個高矮不同的人,排成兩排,每排必須是從矮到高排列,而且第二排比對應的第一排的人高,問排列方式有多少種?

所以自己也考慮了一個演算法,也在網上看到別人的不同的演算法。感覺我這個演算法遍歷效率很高,而且也很簡潔(不敢用最來形容,怕強中更有強中手,當然如果能推匯出公式來求解的話肯定會比我這個演算法快,這個公式是F(n) = (n! / ((n/2)! * (n/2)!))/ (n/2 +1)).

我的演算法思想是這樣的:

(1)把排隊的問題轉換成數字排列問題,類似於0- 11這12個數排成2行。

(2)可以只考慮前一排的情況,因為只要前一排是符合某些條件,剩餘的數按順序放後排自然能滿足條件。

(3)第一排數要符合的條件是:

下面數值表示的是位置,

0   1   2   3   4   5

6   7   8   9 10 11

對於位置0,可能的數的範圍是0

對於位置1,可能的數的範圍是1,2

對於位置2,可能的數的範圍是2,3,4

對於位置3,可能的數的範圍是3,4,5,6

對於位置4,可能的數的範圍是4,5,6,7,8

對於位置5,可能的數的範圍是5,6,7,8,9,10

這樣可以推匯出:

對於位置為n的數,其數的範圍是[n, 2n].

這樣對於0-5的位置的數的範圍就是[{0,1,2,3,4,5}, {0,2,4,6,8,10}],

我們把這個0-5的位置的數當成一個大的數來看待,對其進行++操作,只不過進位的時候是按每個位置的數的範圍來進行進位。

其實可以看成是一個列舉,並且可以用一部分的DP思想,來儲存中間結果

===== 這個解法看不懂。。。 =====

首先把問題抽象,題目變成:給定一組整數,這組整數中每個數值大小不一樣,把這組整數分成兩組,每組按照從小到大排列,要求一組每個成員數值比對應位置上的另外一組的成員數值小。 

    再進一步分解抽象:就是給定一組整數,從這整數中找X個整數出來,使得這X個數之和不大於給定整數陣列之和的一半,如果這個一半的情況找到了,另外一半基本確定,因此,現在題目轉化成從給定陣列中找一個X個數,使得X個數之和小於給定陣列之和的一半。 

    既然問題抽象了,那麼現在來設計演算法,一個最簡單的演算法就是窮舉,人腦簡直是太有限了,讓計算機窮舉去貝,這裡採用遍歷整個陣列,即確定一個首元素後採用棧來確定這個棧裡面的元素是否符合上文中X個數的情況,如果符合就記錄下來,如果不符合就進入下一步選擇。具體演算法是: 


    (1)先對給定陣列Q排序,按照從小到大的順序排列。 
    (2)然後迴圈這個陣列(其時排序後迴圈一半就可以了,理由是找小的一半陣列H,最小的元素肯定小於1/2長度的那個元素),把這個迴圈中的整數A1加入一個棧S,然後從給定陣列Q中找比棧頂元素次大的一個元素最為下一個棧頂元素入棧,迴圈入棧操作。 
    (3)符合情況的記錄:如果這個棧S的元素長度達到給定陣列Q元素長度的一半時,就要判斷這個棧S元素之和是否小於Q陣列之和的一半,如果符合就記錄這個棧S的所有元素,並彈出棧頂元素Top,從Q陣列中找下一個入棧元素,條件時這個入棧元素要比這個出棧元素Top大 
    (4)不符合半組H的情況:當棧S元素長度等於Q陣列長度一半時,並且此時出棧元素是最後一個了(排序後最大的元素),那麼就要出棧兩個元素;當棧S元素長度等於Q陣列長度一半時,並且此時棧S元素之和已大於給定陣列Q之和的一半了,也要出棧兩個元素; 
    (5)退出迴圈情況:當棧S只有小於等於一個元素了,且下一個元素為空,那麼退出當前迴圈;當棧S小於等於兩個元素長度,且下一個元素為陣列Q最後一個元素時,那麼退出當前迴圈;當棧S大於兩個元素長度,且下一個元素為陣列Q最後一個元素時,那麼要出棧兩個元素。 

=====

排列組合解法,是看成catalan數。

只要從12個人中挑選6個人放在第一排,那麼所有的人的位置就都確定了,因為是要求按身高排序的。 
這裡我們的挑選方法以及限制條件是這樣的:12個人先從矮到高排序,然後每個人被選到第一排或者第二排

如果要滿足題意,當且僅當每挑選完一次之後,第二排中的人數不多於第一排中的人數,(這個姑且算對吧。。。)

而這個條件的排列就是 C(12,6) - C(12,5) = 132 ,(那這個又怎麼解釋?)

卡塔蘭數的一般項公式為 C_n = \frac{1}{n+1}{2n \choose n} = \frac{(2n)!}{(n+1)!n!}

另一個表達形式為C_n = {2n\choose n} - {2n\choose n+1} \quad\mbox{ for }n\ge 1

  1   Cn表示所有包含n組括號的合法運算式的個數:((())) ()(()) ()()() (())() (()())

  2   Cn表示有n個節點組成不同構二叉樹的方案數。

證明:

令1表示進棧,0表示出棧,則可轉化為求一個2n位、含n個1、n個0的二進位制數,滿足從左往右掃描到任意一位時,經過的0數不多於1數。顯然含n個1、n個0的2n位二進位制數共有{2n \choose n}個,下面考慮不滿足要求的數目。

考慮一個含n個1、n個0的2n位二進位制數,掃描到第2m+1位上時有m+1個0和m個1(容易證明一定存在這樣的情況),則後面的0-1排列中必有n-m個1和n-m-1個0。將2m+2及其以後的部分0變成1、1變成0,則對應一個n+1個0和n-1個1的二進位制數。反之亦然(相似的思路證明兩者一一對應)。

從而C_n = {2n \choose n} - {2n \choose n + 1} = \frac{1}{n+1}{2n \choose n}。證畢。


3 Cn表示所有在n × n格點中不越過對角線的單調路徑的個數。

4 Cn表示通過連結頂點而將n + 2邊的凸多邊形分成三角形的方法個數。

5 Cn表示對{1, ..., n}依序進出置換個數

6 Cn表示集合{1, ..., n}的不交叉劃分的個數.