卡特兰数简介
卡特兰数又称卡塔兰数,卡特兰数是组合数学中一个常出现在各种计数问题中的数列。以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)的名字来命名。但最早是欧拉在1753年解决凸包划分成三角形问题的时候,推出的Catalan数。
初始值:f(0) = f(1) = 1
递推公式:f(n) = f(0) * f(n - 1) + f(1) * f(n - 2) + …… + f(n - 1) * f(0)
解决的问题:
- 括号化:P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?(答案:catalan[n])
- 出栈次序:一个栈的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?(答案:catalan[n])
- 凸多边形三角划分:在一个凸多边形中,通过若干条互不相交的对角线,把这个多边形划分成了若干个三角形,共有多少种不同的分法?(答案:catalan[n - 2])
- 给定节点组成二叉搜索树:给定N个节点,能构成多少种不同的二叉搜索树?(答案:catalan[n])
- n对括号正确匹配数目:给定n对括号,括号正确配对的字符串数是多少?(答案:catalan[n])
这里我以第二个出栈次序的问题来讲述卡特兰数的由来。首先,我们设f(n)表示序列个数为n的出栈序列种数。(我们假定,最后出栈的元素为k,显然,k取不同值时的情况是相互独立的,也就是求出每种k最后出栈的情况数后可用加法原则,由于k最后出栈,因此,在k入栈之前,比k小的值均出栈,此处情况有f(k-1)种,而之后比k大的值入栈,且都在k之前出栈,因此有f(n-k)种方式,由于比k小和比k大的值入栈出栈情况是相互独立的,此处可用乘法原则,共f(n-k)*f(k-1)种,而k可以取1 ~ n,将所有的f(n-k)*f(k-1)求和便是Catalan递推式。
卡特兰数代码
有了递推关系式,代码简单到爆。
(PS:NR是指catalan数组元素个数的上限)
# include <cstdio>
# include <iostream>
# include <cmath>
# include <cstring>
# include <algorithm>
# include <climits>
using namespace std;
# define FOR(i, a, b) for(int i = a; i <= b; i++)
# define _FOR(i, a, b) for(int i = a; i >= b; i--)
const int NR = 100000;
int n;
long long catalan[NR + 10];
int main()
{
scanf("%d", &n);
catalan[0] = catalan[1] = 1;
FOR(i, 2, n)
FOR(j, 0, i - 1)
catalan[i] += catalan[j] * catalan[i - j - 1];
printf("%lld\n", catalan[n]);
return 0;
}
God Bless You For Ever!