簡介
卡特蘭數(Catalan),又稱明安圖數,是組合數學中一個常出現於各種計數問題的數列。
對應序列為:
\(1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, \cdots\)
滿足
其中 \(C_0=1,C_1=1\)
性質
注:為避免符號重復本文所有公式將以 \(M_n\) 表示卡特蘭數,即明安圖數的拼音首字母。
符合通項公式:
那么可以推出如下公式:
基本原理
先來看一個問題:
對於 \(n\) 對括號,求共有多少種合法的匹配方案數。
其中括號的合法匹配方式為:一個左括號對應一個右括號,且左括號必須要在右括號前面出現。
這里我們用 +1
表示 “ \((\) ” ,用 -1
表示 “ \()\) ”。
那么顯然,對於一組合法的括號序列,該序列的前綴和必然大於或等於 \(0\) ;相反,若該括號序列不合法,其前綴和必然存在小於 \(0\) 的情況。
首先給出一個序列:
()(())()
該序列可以表示為
+1 -1 +1 +1 -1 -1 +1 -1
顯然這是一個非法序列。
對於像這樣的一個非法序列,找到第一個前綴和小於 \(0\) 的括號,並對該前綴中的每一個數進行取反。
上述例子便可以得到
-1 +1 +1 +1 -1 +1 +1 -1
此時該序列中共有 \(3+1+1=5\) 個 +1
和 \(4-1=3\) 個 -1
。不難看出,第一個小於 \(0\) 的前綴和必定為 \(-1\) ,即 \(-1\) 比 \(+1\) 多一個,取反后則 \(-1\) 比 \(+1\) 少一個。這樣總體上看,由於二者數量原本相同,所以 \(+1\) 必定變為 \(n+1\) 個,\(-1\) 則變為 \(n-1\) 個。
由此,該結論可以推廣:
對於 \(n\) 對括號的每種非法匹配序列 \(A\),一定會有一個含有 \(n+1\) 個 \(+1\) 和 \(n-1\) 個 \(-1\) 的序列 \(B\) 與其一一對應。
而序列 \(B\) 的數量可以通過 \(C_{2n}^{n+1}\) 計算,即非法序列的數量為 \(C_{2n}^{n+1}\)。
而序列的總數量為 \(C_{2n}^n\) (從 \(2n\) 個位置中選擇 \(n\) 個位置放左括號,不考慮先后順序),則合法的匹配序列數量為:
由此便推導出了卡特蘭數的通項公式。
應用
P1044 [NOIP2003 普及組] 棧
直接由基本原理可以推出:
公式 1
代碼:
/*
Name: P1044 [NOIP2003 普及組] 棧
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
/*==================================================快讀*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定義變量*/
const int _ = 110;
int n;
int h[_];
/*=============================================自定義函數*/
/*=================================================主函數*/
signed main()
{
h[0] = 1;
h[1] = 1;
n = read();
for (int i = 2; i <= n; i++)
h[i] += h[i - 1] * (4 * i - 2) / (i + 1);
printf("%lld\n", h[n]);
return 0;
}
公式 2
代碼:
/*
Name: P1044 [NOIP2003 普及組] 棧
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
/*==================================================快讀*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定義變量*/
const int _ = 110;
int n;
int h[_];
/*=============================================自定義函數*/
/*=================================================主函數*/
signed main()
{
h[0] = 1;
h[1] = 1;
n = read();
for (int i = 2; i <= n; i++)
for (int j = 0; j < i; j++)
h[i] += h[j] * h[i - j - 1];
printf("%lld\n", h[n]);
return 0;
}
公式 3
其中
並且規定
代碼:
/*
Name: P1044 [NOIP2003 普及組] 棧
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
/*==================================================快讀*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定義變量*/
const int _ = 110;
int n;
int h[_ << 1][_];
/*=============================================自定義函數*/
/*=================================================主函數*/
signed main()
{
n = read();
for (int i = 1; i <= n << 1; i++)
{
h[i][0] = 1;
h[i][i] = 1;
for (int j = 1; j < i; j++)
{
h[i][j] = h[i - 1][j] + h[i - 1][j - 1];
}
}
printf("%lld\n", h[n << 1][n] / (n + 1));
return 0;
}
公式 4
代碼:
/*
Name: P1044 [NOIP2003 普及組] 棧
Solution:
By Frather_
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define int long long
using namespace std;
/*==================================================快讀*/
inline int read()
{
int X = 0, F = 1;
char CH = getchar();
while (CH < '0' || CH > '9')
{
if (CH == '-')
F = -1;
CH = getchar();
}
while (CH >= '0' && CH <= '9')
{
X = (X << 3) + (X << 1) + (CH ^ 48);
CH = getchar();
}
return X * F;
}
/*===============================================定義變量*/
const int _ = 110;
int n;
int h[_ << 1][_];
/*=============================================自定義函數*/
/*=================================================主函數*/
signed main()
{
n = read();
for (int i = 1; i <= n << 1; i++)
{
h[i][0] = 1;
h[i][i] = 1;
for (int j = 1; j < i; j++)
h[i][j] = h[i - 1][j] + h[i - 1][j - 1];
}
printf("%lld\n", h[n << 1][n] - h[n << 1][n - 1]);
return 0;
}
如果還有其他公式歡迎聯系博主!(博主最多推四個了。。)