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)  

 

例如:h(2)=h(0)*h(1)+h(1)*h(0)=1*1+1*1=2
h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=1*2+1*1+2*1=5
 

 

 该递推关系的解为:

另一种递推公式:

如果想看推到过程,请到此处看,此处不再累赘讲述。

应用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.若按第一种加括号方式((A1A2A3)计算,3个矩阵连乘积需要数乘次数为10x100x5+10x5x50=7500.若按第二种加括号方式(A1A2A3))计算,三个矩阵连乘积共需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) 如下: 

1 2 3
4 5 6
1 2 4
3 5 6
1 2 5
3 4 6
1 3 4
2 5 6
1 3 5
2 4 6

 

本题对于给定正整数 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;
}

 

 

 

 

 

 

 

 

 

posted on 2013-04-26 17:08  dztgc  阅读(683)  评论(0编辑  收藏  举报