您的位置:首页 > 其它

卡特兰数在多种问题下的应用 组合数学-Catalan数

2013-11-09 18:12 671 查看
卡特兰数是组合数学中常用的一个数列:

根据《组合数学》中,定理8.1.1:有正1和负1各n个组成的序列,要求部分和总大于0。这样序列个数称作catalan数。

注意:卡特兰数有一个要求,就是正1和负1的个数要相等,否则就是另一个问题了(矩阵格路径数)。

通项公式为:

h(n) = C(2n, n)/(n+1)

递推公式为:

h(0)=1,h(1)=1,

h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)*h(0) ,(n>=2)。

从卡特兰数的递推公式来看,这是一个关于统计划分情况的递归问题。

Catalan数的应用

    Catalan数本质上都可以等价到出入栈问题上。想把这些不同的实际问题抽象成同一个问题,确实有些困难。

1)出栈次序问题

1】n个数有多少种不同的进出栈顺序?

2】n辆火车有多少种不同进出站顺序?

2)加括号问题

1】n对括号有多少种匹配方式?

2】n+1个数相乘,有多少种乘法顺序?(要加n对括号)

3)二叉树问题 

1】给定n个节点,能构成多少种不同的二叉树?

2】已知n个节点组成二叉树的前序序列,共有多少种可能的中序遍历?

转化思路:二叉树的递归问题直接满足catalan数的递推公式。

4)排队顺序问题 

1】长度为2n的Dyck words有多少种?(Dyck words是由n个X和n个Y组成的字符串,其有一个特点:从左往右,对X和Y分别计数,Y的个数始终不大于X的个数。)

转化思路:可以把X看作入栈,Y看作出栈,Y的个数始终不大于X的个数这一性质正好和空栈无法再出栈相一致,所以Dyck words就等价为出入栈序列。

1.1】2n个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?

转化思路:2n个人先从矮到高排序,然后挨个被选到第一排或者第二排,如果要满足题意,当且仅当每挑选完一次之后,第二排中的人数不多于第一排中的人数。

1.2】在一个2*n的格子中填入1到2n这些数值,使得每个格子内的数值都比其右边和上边的所有数值都小,有多少种填法?

1.3】有2n个人排成一行进入剧场。入场费5元。其中n个人只有5元钞票,n个人只有10元钞票(必须找零),剧院无其它钞票,问有多少种买票顺序能让每个人都顺利买票?

转化思路:每来一个5元的人,就能满足1个10元的人,所以始终要满足10元的人数不多于5元的人数。

5)正方形网格路径问题

1】对于一个n*n的正方形网格,每次我们能向右或者向上移动一格,求从左下角到右上角的所有在副对角线右下方的路径总数。

转化思路:把一条水平边标记为+1,垂直边标记为-1,那么就组成一个n个+1和n个-1的序列,我们所要保证的是前k步中水平边的个数不小于垂直边的个数。

1.1】一位大城市的律师在他住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果他从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?

6)将多边形划分问题

1】n+2条边的凸多边形能划分成n个三角形,有多少种划分方法?

转化思路:首先知道需要加n-1条弦。这里以边为研究对象,一共有n+2条边。加弦的目的是把两个挨着的边括起来。那么这就成了在两个边两侧加括号了,n+2条边要加n对括号。

2】n层阶梯划分为n个矩形,一共有多少种分法?

2】圆桌周围有2n个人,他们两两握手,但没有交叉的方案数。(每个人最多握一次手)

转化思路:握手行为是由2个人共同完成的,设定这一行为有主动方(入栈)和被动方(出栈),假设握手行为是根据座次号(1-2n)按时间顺序发生的,如果那人是主动方,他会提出握手要求(+1);如果那人是被动方,他会接受握手要求(-1)。因为握手不能交叉,所以这就成了出入栈序列。

    从没有交叉的图形角度来看,一旦确定了某个握手行为,握手的双方就划分了两个隔离的部分,这两个部分内部再进行握手。

2.1】在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数。

Catalan数的计算:

注意两点

1、上面递推函数h(n)中的n是单独一个栈操作方向的个数,总操作个数是2*n。

2、h(n)函数的增长速度非常的快。所以在计算过程中很容易溢出。即使是64位的数据类型也得溢出。

代码:下面是一种求解,在求解过程中对结果%1000000007取余了。

int  fun(int n)
{
int catalan;
long long *k = new long long[n+1];

for(int i=0;i<=n;i++)
k[i] = 0;
k[0] = 1;
k[1] = 1;
for(int j=2;j<=n;j++)
for(int i=0;i<j;i++)
{
k[j] = (k[j] + k[i]*k[j-1-i])%1000000007;
}

catalan = k
%1000000007;

delete []k;
return catalan;
}


本文参考自blog.csdn.net/hardbrave/article/details/6713915
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: