Catalan数计算及应用
问题描述:卡塔兰数,是组合数学中一个常出现在各种计数问题中出现的数列。输入一个整数n,计算h(n)。
其递归式如下:h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (其中n>=2,h(0) = h(1) = 1)
该递推关系的解为:
另一种递推公式:
如果想看推到过程,请到此处看,此处不再累赘讲述。
应用1描述:n对括号有多少种匹配方式?
应用2描述:矩阵链乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?
应用3描述:一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?
应用4描述:n+1个叶子节点构成的二叉树,共有多少种情形?
应用5描述:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?
应用6描述:求一个凸多边形区域划分成三角形区域的方法数?
应用7描述:有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?
这些问题的具体思路可见此文章,讲解的比较清楚。
此文主要说一下它们之间的具体联系。
——————————————————————————————————————思维延伸————————————————————————————————————————————————
先看一下应用2中说的矩阵链乘,捎带着说一句此问题的实际应用。
为了说明在计算矩阵连乘积时,加括号方式对整个计算量的影响,考察三个矩阵{A1,A2,A3}的连乘积的例子。
设三个矩阵的维数分别为10x100,100x5和5x50.若按第一种加括号方式((A1A2)A3)计算,3个矩阵连乘积需要数乘次数为10x100x5+10x5x50=7500.若按第二种加括号方式(A1(A2A3))计算,三个矩阵连乘积共需100x5x50+10x100x50=75000次数乘。由此看见,加括号的不同方式,即计算次序对计算量有很大影响。
这样自然就会想到矩阵连乘时的最优计算次序,这个问题在后面的文章中会讲到。
——————————————————————————————————————————————————————————————————————————————————————————
在上述应用中,很自然的就能看到应用1,应用2,应用3和应用7,是同一类问题(都是加括号问题)。
而实际上其余问题也能转化。
一个表达式的完全加括号方式对应于一棵完全二叉树——表达式的语法树。
例如,完全加括号的矩阵连乘积((A1(A2A3))(A4(A5A6))),所对应的语法数如图1.
图1
语法树中的每一个叶节点表示表达式中的一个原子。在语法树中,若一个结点有一个表示表达式El的左子树,以及一个表示表达式Er的右子树,则以该结点为根的子树表示表达式(ElEr)。则n个原子的完全加括号表达式对应于唯一的一棵有n个叶结点的语法树。
这棵语法数,其实就是应用4.
接下来再看应用6
凸多边形{v0,v1,...vn-1}的三角区域也可以用语法树来表示。
例如,图2
该语法树的根节点为边V0V6.三角部分的弦组成其余的内节点。多边形除V0V6边外的各边都是语法树的一个叶节点。树根V0V6是三角形V0V3V6的一条边。该三角形将多边形分成三个部分:三角形V0V3V6,凸边形{V0,V1,..V3}和凸边形{v3,v4,..,v6}.三角形另外两条边,即弦v0v3和v3v6为根的两个儿子。以它们为根的子树表示凸多边形{V0,V1,..V3}和{v3,v4,..,v6}的三角部分。
在一般情况下,凸n边形的三角部分对应于一棵有n-1个叶节点的语法数。因此n个矩阵的完全加括号乘积与凸(n+1)边形中的三角部分之间存在一一对应关系。
矩阵连乘积A1,A2,...,An中的每个矩阵Ai对应于凸(n+1)边形中的一条边vi-1vi,.三角部分的一条弦vivj,i<j,对应于矩阵连乘积A[i+1,j].
这里介绍一种新的应用:
Description
设 n 是一个正整数, 2*n 的标准二维表是由正整数 1, 2, ..., 2*n 组成的2*n 数组, 该数组的每行从左到右递增, 每列从上到下递增. 2*n 的标准二维表全体记为 Tab(n). 譬如: 当 n = 3 时 Tab(3) 如下:
|
|
|
|
|
本题对于给定正整数 n, 计算 Tab(n) 中 2*n 的标准二维表的个数.
此题有一个实际应用,即按照身高排队的问题。
此题可以等价为应用1
可以把第一行的数当成左括号,第二行当成对应的右括号。一种括号的匹配方式对应了一个二维表。
例如
把((( ))) 从前向后编号 1,2,3,4,5,6 ,然后把 "(" 对应的数放到第一行,")"的数放到第二行,则为
1 | 2 | 3 |
4 | 5 | 6 |
很容易解释:
因为编号是递增的,则对于每一对括号的"("总是满足递增,且同一对括号,")"必然在"("的右边,则")"的编号必然大于"("。
则二维表的个数也符合卡特兰树。
代码:
int standard2DTable(int n) { int *h = new int(n+1); h[0]=h[1]=1; for(int i=2;i<=n;i++) { h[i]=0; for(int j=0;j<i;j++) h[i]+=h[j]*h[i-j-1]; } int result = h[n]; delete [] h; return result; }