問題描述:
設n 是一個正整數。2xn的標准2維表是由正整數1,2,…,2n 組成的2xn 數組,該數組的每行從左到右遞增,每列從上到下遞增。2xn的標准2維表全體記為Tab(n)。
例如,當n=3時Tab(3)如下:
思路分析:首先明確一下每行的數總是左邊小於后面,上面小於下面,以上面的第一種情況進行分析,我們把第一行的數字對應為1表示進棧
。第二行的數字對應為-1表示出棧。我們知道一般情況進棧和出棧時棧里面的元素個數大於等於0,那么數字1看成進棧-1看成出棧,則總數之和要大於等於0。
即進出棧操作任何時刻進棧次數大於等於出棧的次數。那么上表的第一行第一個元素表示第一次是進棧操作,下面的4對應的是第一次進棧的元素在第四次出棧,第一行第二列2
表示第二次操作是進棧對應對應下面的5表示第五次操作是出棧,即把第二次進棧操作的元素出棧,依次類推,第三次是進棧操作,第六次把第三次進棧的元素彈出棧。
所以我們可以把表看出是元素的進出棧的操作,則tab(n)表示求元素個數為n的所有可能進出棧的操作。於是問題轉換為n個元素所有可能進出棧的情況。
而求進出棧的所有可能情況的方法就是卡特蘭數。下面簡單介紹一下卡特蘭數。
事實上,可以認為問題是,任意兩種操作,要求每種操作的總次數一樣,且進行第k次操作2前必須先進行至少k次操作1。我們假設一個人在原點,操作1是此人沿右上角45°走一個單位(一個單位設為根號2,這樣他第一次進行操作1就剛好走到(1,1)點),操作2是此人沿右下角45°走一個單位。第k次操作2前必須先進行至少k次操作1,就是說明所走出來的折線不能跨越x軸走到y=-1這條線上!在進行n次操作1和n此操作2后,此人必將到到達(2n,0)!若無跨越x軸的限制,折線的種數將為C(2n,n),即在2n次操作中選出n次作為操作1的方法數。

現在只要減去跨越了x軸的情況數。對於任意跨越x軸的情況,必有將與y=-1相交。找出第一個與y=-1相交的點k,將k點以右的折線根據y=-1對稱(即操作1與操作2互換了)。可以發現終點最終都會從(2n,0)對稱到(2n,-2)。由於對稱總是能進行的,且是可逆的。我們可以得出所有跨越了x軸的折線總數是與從(0,0)到(2n,-2)的折線總數。而后者的操作2比操作1要多0-(-2)=2次。即操作1為n-1,操作2為n+1。總數為C(2n,n-1)。

catalan數和出棧序列的對應:
動態規划:我們把n個元素的出棧個數的記為f(n), 那么對於1,2,3, 我們很容易得出:
f(1) = 1 //即 1
f(2) = 2 //即 12、21
f(3) = 5 //即 123、132、213、321、231
然后我們來考慮f(4), 我們給4個元素編號為a,b,c,d, 那么考慮:元素a只可能出現在1號位置,2號位置,3號位置和4號位置(很容易理解,一共就4個位置,比如abcd,元素a就在1號位置)。
分析:
1) 如果元素a在1號位置,那么只可能a進棧,馬上出棧,此時還剩元素b、c、d等待操作,就是子問題f(3);
2) 如果元素a在2號位置,那么一定有一個元素比a先出棧,即有f(1)種可能順序(只能是b),還剩c、d,即f(2), 根據乘法原理,一共的順序個數為f(1) * f(2);
3) 如果元素a在3號位置,那么一定有兩個元素比1先出棧,即有f(2)種可能順序(只能是b、c),還剩d,即f(1),
根據乘法原理,一共的順序個數為f(2) * f(1);
4) 如果元素a在4號位置,那么一定是a先進棧,最后出棧,那么元素b、c、d的出棧順序即是此小問題的解,即 f(3);
結合所有情況,即f(4) = f(3) + f(2) * f(1) + f(1) * f(2) + f(3);
為了規整化,我們定義f(0) = 1;於是f(4)可以重新寫為:
f(4) = f(0)*f(3) + f(1)*f(2) + f(2) * f(1) + f(3)*f(0)
然后我們推廣到n,推廣思路和n=4時完全一樣,於是我們可以得到:
f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-1)*f(0)
下面是推導式,不給證明了
另類遞推式:
h(n)=h(n-1)*(4*n-2)/(n+1);
遞推關系的解為:
h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
遞推關系的另類解為:
h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)
那么我們了解了卡特蘭數之后就可以寫代碼了,demo如下
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 int tan(int n){ 5 if(n==1|| n==0) 6 return 1; 7 else if(n==2) 8 return 2; 9 else { 10 return tan(n-1)*(4*n-2)/(n+1); 11 } 12 13 } 14 int main() 15 { 16 int n; 17 cin >> n; 18 cout << tan(n); 19 return 0; 20 }
但是這個代碼只是簡單運用了卡特蘭數解決了n較小的問題,但是如下
其前幾項為 : 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...
所以n較大的時候Int就無法表示,下面是處理大整數后的思路(關於大整數的內容可以我的參考這篇博客(https://www.cnblogs.com/henuliulei/p/9867127.html))
下面是具體的大卡特蘭數
1 #include<bits/stdc++.h> 2 using namespace std; 3 char array1[200]; 4 char array2[200]; 5 int sum[400]; 6 string f(string s1,string s2)//大整數乘法 7 { 8 reverse(s1.begin(),s1.end()); 9 reverse(s2.begin(),s2.end()); 10 memset(array1,0,sizeof(s1)); 11 memset(array2,0,sizeof(s2)); 12 memset(sum,0,sizeof(sum)); 13 int l1=s1.length(); 14 int l2=s2.length(); 15 strcpy(array1,s1.c_str()); 16 strcpy(array2,s2.c_str()); 17 for(int i=0;i<l1;i++){ 18 for(int j=0;j<l2;j++){ 19 sum[i+j]+=((int)array1[i]-48)*((int)array2[j]-48); 20 } 21 } 22 int f=0; 23 while(true){ 24 25 if(f<l1+l2){ 26 int de=sum[f]/10; 27 sum[f]=sum[f]%10; 28 sum[f+1]+=de; 29 f++; 30 } 31 else{ 32 break; 33 } 34 } 35 string a=""; 36 int ig=0; 37 for(int i=l1+l2-1;i>=0;i--){ 38 if((char)sum[i]+48!='0'){ 39 ig=1; 40 } 41 if(ig==1){ 42 a+=(char)sum[i]+48; 43 } 44 } 45 return a; 46 } 47 string div(string a,int b){//大整數除法 48 49 char s[1000]; 50 memset(s,0,sizeof(s)); 51 unsigned long long sum=0; 52 for(int i=0;a[i];i++) 53 { 54 sum=sum*10+a[i]-'0'; 55 s[i]=sum/b+'0'; 56 sum=sum%b; 57 } 58 int j=0; 59 string as=""; 60 while(s[j]=='0') 61 j++; 62 63 for(;j<a.size();j++) 64 as+=s[j];//商 65 return as; 66 67 } 68 int t(string n)//把字符串變為int類型 69 { 70 int a=0; 71 int s=1; 72 for(int i=n.length()-1;i>=0;i--){ 73 a+=((int)n[i]-48)*(s); 74 s*=10; 75 } 76 return a; 77 } 78 string tan(string n){//遞歸實現卡特蘭數 79 if(n=="1"|| n=="0") 80 return "1"; 81 else if(n=="2") 82 return "2"; 83 else { 84 int s=t(n); 85 s=(s+1); 86 int st=t(n)*4-2; 87 int s1=t(n); 88 s1--; 89 char a[100]; 90 char a1[100]; 91 itoa(st,a,10); 92 itoa(s1,a1,10); 93 string s2=tan(a1); 94 string re=div(f(s2,a),s); 95 return re; 96 } 97 } 98 int main() 99 { 100 string s1; 101 string s2; 102 string s3; 103 cin >> s1; 104 cout << tan(s1); 105 }