淺談卡特蘭數(Catalan number)的原理和相關應用


一、卡特蘭數(Catalan number)

1.定義

組合數學中一個常出現在各種計數問題中出現的數列(用c表示)。以比利時的數學家歐仁·查理·卡特蘭的名字來命名;

2.計算公式

(1)遞推公式

c[n]=Σ(0≤k<n)c[k]c[n-k-1],邊界條件為c[0]=1;
其遞推解為c[n]=C(2n,n)/(n+1),即卡特蘭數的通項公式,其中C表示數的組合;
根據組合公式我們可以化簡得c[n]=2n
(2n-1).....(n+2)/n!;

(2)另類遞推式

c[n]=c[n-1](4n-2)/(n+1),邊界條件為c[0]=1;
其遞推解為c[n]=C(2n,n)-C(2n,n-1),C表示數的組合;

(3)其他公式

c[n]=(Σ(0≤i≤n)C(i,n)^2)/(n+1);

ps:通過展開可以發現它們的本質是一樣的;

3.簡單應用

(1)求解路徑方案數:如圖所示,從原點(0,0)到點B(n,n),只能向右或向上進行長度為一個單位的移動,路線一直處於y=x之下(不越過直線y=x)的不同路徑方案數;

Solution:每個n的解都可以看做先前的解數(再向右向上即為所求)加上不觸及各個y=x上的點到達B點的方案數,可以發現其遞推公式即為卡特蘭數計算公式;

(2)求01串的個數:n個0與n個1構成的序列方案數,使得任何一個前綴0的個數不少於1的個數;

Solution:將0看做在坐標系中向右走一步,1看做向上走一步,則問題可化簡為從原點到(n,n)所有路線中一直處於y=x之下(不越過直線y=x)的不同路徑方案數,與上題相同,方案數即為對應n的卡特蘭數;

(3)給定節點求解二叉樹的個數:已知由n個節點,求形成不同的二叉樹有多少種?

Solution:將向左生成子樹看做0,向右生成字數看成1,則問題化簡為求n/2個0和n/2個1構成數串的不同方案數,即上題無條件解的一半,與上題答案相同,形成不同的樹的個數即為對應n的卡特蘭數;

(4)求凸邊形進行三角剖分的不同方案數:在一個有n+3條邊的凸多邊形中,求通過若干條互不相交的對角線,把這個多邊形划分成若干個三角形的不同方案數。

Solution:因為每一條邊都一定是剖分后的三角形的一條邊,任意一條邊都會把多邊形分成兩個小多邊形,那么根據乘法原理,解即為划分成不同多邊形的方案數對應小多邊形的划分方案數之和,即f[n]=Σ(3≤k≤n-3)f[k]f[n-k-1],可以發現解數即為n對應的卡特蘭數;

(5)n對括號正確匹配數目:給定n對括號,求括號正確配對的字符串數;

Solution:因為是匹配問題,那么最后一個左括號必然有唯一右括號與其匹配,假設f[n]為n對括號的正確配對數目,那么有遞推關系f[n]=f[0]f[n-1]+f[1]f[n-2]+...+f[n-1]f[0],顯然f[n]是n對應的卡特蘭數。

4.卡特蘭數的擴展(折線原理)

對於在n位的2進制中,有m個0,其余為1的catalan數為:C(n,m)-C(n,m-1)。其可由應用(1)證明;

二、相關應用

1.[NOIP2003]棧

題解隨筆:http://www.cnblogs.com/COLIN-LIGHTNING/p/8481413.html

2.[洛谷P1722]矩陣II

Description

給定一個長度為2n的數列,現讓你自由地放入紅色算籌和黑色算籌,求對於所有的i(1<=i<=2n),使第1~i格中紅色算籌個數大於等於黑色算籌的方案數;
輸入:n
輸出:對100取模后的方案數;

Solution

1.將紅看做入棧操作,黑看為出棧操作,問題即為求不同的合法的入棧出棧序個數;
2.那么解法與上題相同,易得解即為對應的卡特蘭數;
3.我們可以考慮用遞推式(1)求卡特蘭數,取模更容易;

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int main(){
	unsigned long long ctl[110],i,j,k,n;
	memset(ctl,0,sizeof(ctl));
	ctl[0]=ctl[1]=1;
	ctl[2]=2;
	scanf("%d",&n);
	for(i=3;i<=n;++i)
		for(j=0;j<i;++j){
			ctl[i]+=ctl[j]*ctl[i-j-1];
			ctl[i]%=100;
		}
	printf("%d\n",ctl[n]);
	return 0;
} 

3.[洛谷P1976]雞蛋餅

Description

最近小 x 又發現了一個關於圓的有趣的問題:在圓上有2N 個不同的點,小 x 想用 N 條線段把這些點連接起來(每個點只能連一條線段), 使所有的線段都不想交,他想知道這樣的連接方案有多少種?
輸入輸出格式
輸入格式:有且僅有一個正整數 N
輸出格式:要求的方案數(結果 mod 100000007)。

Solution

1.可將問題化簡為求n+3邊形進行三角剖分的方案數,解法就顯而易見了;
2.對1e8+7取模可以考慮使用遞推公式(1)解決;

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int main(){
	unsigned long long ctl[32768],i,j,k,n;
	memset(ctl,0,sizeof(ctl));
	ctl[0]=ctl[1]=1;
	ctl[2]=2;
	scanf("%d",&n);
	for(i=3;i<=n;++i)
		for(j=0;j<i;++j){
			ctl[i]+=ctl[j]*ctl[i-j-1];
			ctl[i]%=100000007;
		}
	printf("%d\n",ctl[n]);
	return 0;
} 

4.[AHOI2012]樹屋階梯

題解隨筆:http://www.cnblogs.com/COLIN-LIGHTNING/p/8481432.html

5.[HNOI2009]有趣的數列

題解隨筆:http://www.cnblogs.com/COLIN-LIGHTNING/p/8481448.html

6.[SCOI2010]生成字符串(卡特蘭數的擴展)

題解隨筆:http://www.cnblogs.com/COLIN-LIGHTNING/p/8481454.html


免責聲明!

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



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