Bootstrap

卡特兰数公式

     今天在刷题时,遇到了一个题:设栈的初始状态为空,当字符序列a3_作为栈的输入时,输出长度为3的且可以用作C语言标识符的字符串序列有()个。开始我是一个一个试试的,但是如果序列较多时,这种情况并不好计算。看了下底下的大神解析,说是用卡特兰数公式可以很快解决,所以就找来了这篇博文。
1.什么是卡特兰数
卡特兰数的递归公式是:F(n)=∑(k=1,2...n)F(k-1)*F(n-k)=∑(k=0,1,2...n-1)F(k)*F(n-k+1)
卡特兰数的一般公式是:F(n)=C(2n,n)/(n+1)
2.推导一般公式
引例: ①已知有编号为1到n的n个元素,将它们顺序入栈,请问共有多少种出栈序列?
          ②有一个n*n的棋盘,从左下角走到右上角而不穿过主对角线有多少种走法?
先看第一个问题,n个元素入栈出栈,总共要经历n次入栈和n次出栈,入栈的元素个数不能小于出栈的元素个数,这就相当于对2n个操作进行排列。我们可以将问题转化为,有n个0和n个1,对它们进行排序,然后从左到右扫描,要求累计的1的个数要不小于累计的0的个数,试问满足此要求的序列有多少个?
问题继续转化,我们可以先求出这2n个数一共可以组成多少种排列,再减去不符合要求的排列(不符合要求的排列就是从左到右扫描累计的1的个数小于累计的0的个数),前者的值为C(2n,n),接下来我们求后者的值。
不符合要求的排列满足:在某个奇数位置如2m+1,累计的1的个数比0小,此时1的个数为m,0的个数为m+1。此时后面还有2n-2m-1个数,其中包括n-m个1和n-m-1个0,如果将这n-m个1换为0,n-m-1个0换为1,那么该序列就包括n+1个0和n-1个1,也即,n+1个0和n-1个1必对应一个不符合要求的数,这个数量为C(2n,n+1)。
所以出栈序列共有C(2n,n)-C(2n,n+1)=C(2n,n)/(n+1)种。
对于第二个问题,因为从左下角走到右上角需要向右走n步,向上走n步,且要求向右走累计的步数必须不小于向上走累计的步数才能保证不穿过主对角线,所以这两个问题实质上是相同的。
3.推导递归公式
引例:③n个元素的二叉查找树共有多少种?
设n个元素的二叉查找树共有F(n)个,根节点可以为从1到n的任意一个节点,设为k,则其左右子树中元素的个数分别为k-1和n-k,其左右子树的二叉查找树分别有F(k-1)和F(n-k)种,故F(n)=∑(k=1,2...n)F(k-1)*F(n-k)=∑(k=0,1,2...n-1)F(k)*F(n-k+1)。
4.如何证明卡特兰数的递归公式和一般公式的值是相同的
这里我们证明引例②的值与卡特兰数的递归公式的值相同即可。
设有一个n*n的棋盘,主对角线为L,从左下角到右上角不穿过L的所有路径不算起点,一定有第一次接触到L的位置,设这个位置为M(x,x),则其上一个位置必为(x,x-1),第一点(0,0)的下一步一定是(1,0),从(0,0)到M之间的任何点都不在主对角线L上。我们观察从点(1,0)到点(x,x-1)的正方形,设其对角线为L1,从(1,0)到(x,x-1)的所有路径都不能经过对角线L1,否则它就会触碰到主对角线L,这样在这个区域中满足条件的路径数量就是一个同构的子问题,解应该为F(x-1),从M到右上角终点的路径数量也是一个同构的子问题,解为F(n-k),而第一个接触主对角线的点可以是从(1,1)到(n,n)的任意一个点,故F(n)=∑(k=1,2...n)F(k-1)*F(n-k)=∑(k=0,1,2...n-1)F(k)*F(n-k+1)。
(0,0)到(n,n)不经主对角线的路径数F(n)=∑(1,0)到(x,x-1)不经该区域主对角线的路径数F(x-1)*(x,x)到(n,n)不经主对角线的路径数F(n-x)
5.卡特兰数还能解决什么问题
矩阵链乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?
在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?
12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?(淘宝笔试题)
问题分析:
我们先把这12个人从低到高排列,然后,选择6个人排在第一排,那么剩下的6个肯定是在第二排。
用0表示对应的人在第一排,用1表示对应的人在第二排,那么含有6个0,6个1的序列,就对应一种方案。
比如000000111111就对应着
第一排:0 1 2 3 4 5
第二排:6 7 8 9 10 11
010101010101就对应着
第一排:0 2 4 6 8 10
第二排:1 3 5 7 9 11
问题转换为,这样的满足条件的01序列有多少个。
观察1的出现,我们考虑这一个出现能不能放在第二排,显然,在这个1之前出现的那些0,1对应的人
要么是在这个1左边,要么是在这个1前面。而肯定要有一个0的,在这个1前面,统计在这个1之前的0和1的个数。
也就是要求,0的个数大于1的个数。
;