關於Catalan數


仍然是數學

卡特蘭數是一個非常神奇的東西

序列長這樣↓

1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786……(從第零項開始)

通常比較常用的應該是遞推式和組合數的求法

遞推式:

f(n)=f(n-1)*(4n-2)/(n+1)

其中令f(0)=1

組合數:

f(n)=C(2n,n)/(n+1)

通常情況下,碰到一道題,你不確定是不是卡特蘭數,可以手動或寫個暴力跑出來比對結果,一般來說前七八項符合條件,那就沒什么問題了,反正我碰到的就是這個樣子2333

我碰到過的卡特蘭數題好像不是很多。吧

【一】二叉樹

求n個節點構成的不同二叉樹個數(n≤8000)

這是一道很經典的題目,很多地方都有,n的范圍可以到七八千左右吧

最朴素的算法就是暴力搜索了,雖然拿不了幾分==

把前面幾個算出來,可以發現就是卡特蘭數的序列,然后就可以直接寫了

發現是卡特蘭數不難,但是關鍵選擇計算卡特蘭數的方法

首先n那么大,顯然是要高精度的,高精度的話復雜度就和答案長度有關了,n為七八千的時候,答案長度會達到四五千,是非常大的一個數,所以必須要考慮壓位

遞推式的話,時間復雜度為O(nlen)也就是遞推復雜度乘上高精度乘除的復雜度

下面放上fp代碼↓

 const tt=1000000000;
 type arr=array[0..550]of int64;
 var n:longint;
     ans,ans1:arr;
 procedure init;
  var i:longint;
   begin
    assign(input,'secret.in');reset(input);
    assign(output,'secret.out');rewrite(output);
    readln(n);
   end;
 function mul(a:arr;x:longint):arr;
  var i:longint;
      c:arr;
   begin
    fillchar(c,sizeof(c),0);
    c[0]:=a[0];
    for i:=1 to c[0] do
     begin
      inc(c[i],a[i]*x);
      inc(c[i+1],c[i] div tt);
      c[i]:=c[i] mod tt;
     end;
    if c[c[0]+1]>0 then inc(c[0]);
    while (c[0]>1)and(c[c[0]]=0) do dec(c[0]);
    exit(c);
   end;
 function divx(a:arr;x:longint):arr;
  var i:longint;
      c:arr;
      yu:int64;
  begin
   fillchar(c,sizeof(c),0);
   c[0]:=a[0];yu:=0;
   for i:=c[0] downto 1 do
    begin
     yu:=a[i] mod x;
     inc(a[i-1],yu*tt);
     c[i]:=a[i] div x;
    end;
   if c[c[0]+1]>0 then inc(c[0]);
   while (c[c[0]]=0)and(c[0]>1) do dec(c[0]);
   exit(c);
  end;
 procedure main;
  var i:longint;
   begin
    ans[0]:=1;ans[1]:=1;
    for i:=2 to n do
      ans:=divx(mul(ans,4*i-2),i+1);
   end;
 procedure print;
  var i,j:longint;
      s:string;
  begin
   write(ans[ans[0]]);
   for i:=ans[0]-1 downto 1 do
    begin
     str(ans[i],s);
     for j:=9 downto length(s)+1 do write(0);
     write(s);
    end;
   close(input);close(output);
  end;
 begin
  init;
  main;
  print;
 end.

如果選組合數的話,按照組合數定義一個個跑的話復雜度和遞推差不多==

所以可以進行拆分質因子,因為任何一個合數都可以拆分成若干個質因子相乘的形式,質數看成它本身即↓

X=(p1^a1)*(p2^a2)*(p3^a3)*(p4^a4)*……*(pk^ak)  (pi為質數)

所以通過枚舉這些質因子求積,連乘的可以通過快速冪來實現,調用高精度乘法的次數減少了,只用在快速冪和冪的連乘,質因子只有根號個,復雜度就妥妥的降下來了,代碼看下面↓

 const tt=1000000000;
 type arr=array[0..550]of int64;
 var n:longint;
     hash:array[0..16005]of longint;
     vis:array[0..16005]of boolean;
     ans:arr;
 procedure init;
  begin
   assign(input,'secret.in');reset(input);
   assign(output,'secret.out');rewrite(output);
   readln(n);
  end;
 procedure maker;
  var i,j:longint;
   begin
    fillchar(vis,sizeof(vis),1);
    for i:=2 to trunc(sqrt(2*n)) do
     for j:=2 to 2*n div i do
      if vis[i] then vis[i*j]:=false;
   end;
 function count(x,p:longint):longint;
  begin
   count:=0;
   while x>=p do
    begin
     inc(count,x div p);
     x:=x div p;
    end;
  end;
 function mul(a,b:arr):arr;
  var i,j:longint;
      c:arr;
  begin
   fillchar(c,sizeof(c),0);
   c[0]:=a[0]+b[0]-1;
   for i:=1 to a[0] do
    for j:=1 to b[0] do
     begin
      inc(c[i+j-1],a[i]*b[j]);
      inc(c[i+j],c[i+j-1] div tt);
      c[i+j-1]:=c[i+j-1] mod tt;
     end;
    if c[c[0]+1]>0 then inc(c[0]);
    while (c[c[0]]=0)and(c[0]>1) do dec(c[0]);
    exit(c);
   end;
 function power(a,b:longint):arr;
  var sum,w:arr;
   begin
    w[0]:=1;w[1]:=a;
    sum[0]:=1;sum[1]:=1;
    while b<>0 do
     begin
      if b and 1=1 then sum:=mul(sum,w);
      w:=mul(w,w);
      b:=b>>1;
     end;
    exit(sum);
   end;
 procedure main;
  var i:longint;
   begin
    maker;
    fillchar(hash,sizeof(hash),0);
    for i:=2 to n*2 do
     if vis[i] then hash[i]:=count(n*2,i)-count(n,i)-count(n+1,i);
    ans[1]:=1;ans[0]:=1;
    for i:=2 to 2*n do
     if hash[i]<>0 then ans:=mul(ans,power(i,hash[i]));
   end;
 procedure print;
  var i,j:longint;
      s:string;
  begin
   write(ans[ans[0]]);
   for i:=ans[0]-1 downto 1 do
    begin
     str(ans[i],s);
     for j:=length(s)+1 to 9 do write(0);
     write(s);
    end;
   close(input);close(output);
  end;
 begin
  init;
  main;
  print;
 end.

所以就解好了

【二】出棧次序

一個棧(無窮大)的進棧序列為1,2,3,…,n,求不同的出棧序列個數

和上面那個題也是一個思路,最后答案序列是卡特蘭數

類似的變形題有買票找零之類的

題目描述長這樣↓

一場激烈的足球賽開始前,售票工作正在緊張的進行中,每張球票為 50 元,現有 2n 個人排隊等待購票,其中有 n 個人手持 50 元的鈔票,另外 n 個人手持 100 元的鈔票,假設開始售票時售票處沒有零錢,問 2n 個人有多少種排隊方式,使售票處不至出現找不開錢的局面。

我最開始做的時候是找規律,發現答案剛好是卡特蘭數就寫了,后來發現可以轉換成進出棧的問題就是50元看成進棧,100元的時候把找回的50元看做一次出棧就一毛一樣了。。

【三】凸多邊形的三角划分

在一個凸多邊形中,通過若干條互不相交的對角線,把這個多邊形划分成了若干個三角形。求n多邊形不同划分的方案數f(n)。

題目變形有在圓上選擇2n個點,將這些點成對連接起來使得所得到的n條線段不相交的方法數之類的

【四】括號化

矩陣連乘: P=A1*A2*A3*……*An,依據乘法結合律,不改變其順序,只用括號表示成對的乘積,求不同括號化的方案數

方案數是f(n-1),然而並沒做過這類的題==

好像就沒有其他什么東西了誒

【寫的有漏洞的,歡迎路過大神吐槽】

2016-08-09 22:35:33

Ending.

 


免責聲明!

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



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