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

卡特兰数是组合数学中常用的一个数列:
根据《组合数学》中,定理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个节点,能构成多少种不同的二叉树?


转化思路:二叉树的递归问题直接满足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[n]%1000000007;
     
    delete []k;
    return catalan;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值