卡特兰数(转)

来源:互联网 发布:q版泡泡堂mac 编辑:程序博客网 时间:2024/06/04 08:15
Catalan数

  中文:卡特兰数

  原理:

  令h(1)=1,h(0)=1,catalan数满足递归式:

  h(n)= h(1)*h(n-1) + h(2)*h(n-2) + ... + h(n-1)h(1) (其中n>=2)

  另类递归式:

  h(n)=((4*n-2)/(n+1))*h(n-1);

  该递推关系的解为:

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

  我并不关心其解是怎么求出来的,我只想知道怎么用catalan数分析问题。

  我总结了一下,最典型的四类应用:(实质上却都一样,无非是递归等式的应用,就看你能不能分解问题写出递归式了)

  1.括号化问题。

  矩阵链乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?(h(n)种)

  2.出栈次序问题。

  一个栈(无穷大)的进栈序列为1,2,3,..n,有多少个不同的出栈序列?

  类似:有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈)

  3.将多边行划分为三角形问题。

  将一个凸多边形区域分成三角形区域的方法数?

  类似:一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果她

  从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?

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

  4.给顶节点组成二叉树的问题。

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

  (能构成h(N)个)

实质上都是递推等式的应用

括号化

矩阵链乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?(h(n-1)种)[3]

出栈次序

一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?[4-5]
常规分析
首先,我们设f(n)=序列个数为n的出栈序列种数。同时,我们假定,从开始到栈第一次出到空为止,这段过程中出栈的序数最大的是k。特别地,如果栈直到整个过程结束时才空,则k=n
首次出空之前第一个出栈的序数k将1~n的序列分成两个序列,其中一个是1~k-1,序列个数为k-1,另外一个是k+1~n,序列个数是n-k。
此时,我们若把k视为确定一个序数,那么根据乘法原理,f(n)的问题就等价于——序列个数为k-1的出栈序列种数乘以序列个数为n - k的出栈序列种数,即选择k这个序数的f(n)=f(k-1)×f(n-k)。而k可以选1到n,所以再根据加法原理,将k取不同值的序列种数相加,得到的总序列种数为:f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0)。
看到此处,再看看卡特兰数的递推式,答案不言而喻,即为f(n)=h(n)= C(2n,n)/(n+1)= c(2n,n)-c(2n,n+1)(n=0,1,2,……)。
最后,令f(0)=1,f(1)=1。
非常规分析
对于每一个数来说,必须进栈一次、出栈一次。我们把进栈设为状态‘1’,出栈设为状态‘0’。n个数的所有状态对应n个1和n个0组成的2n位二进制数。由于等待入栈的操作数按照1‥n的顺序排列、入栈的操作数b大于等于出栈的操作数a(a≤b),因此输出序列的总数目=由左而右扫描由n个1和n个0组成的2n位二进制数,1的累计数不小于0的累计数的方案种数。
在2n位二进制数中填入n个1的方案数为c(2n,n),不填1的其余n位自动填0。从中减去不符合要求(由左而右扫描,0的累计数大于1的累计数)的方案数即为所求。
不符合要求的数的特征是由左而右扫描时,必然在某一奇数位2m+1位上首先出现m+1个0的累计数和m个1的累计数,此后的2(n-m)-1位上有n-m个 1和n-m-1个0。如若把后面这2(n-m)-1位上的0和1互换,使之成为n-m个0和n-m-1个1,结果得1个由n+1个0和n-1个1组成的2n位数,即一个不合要求的数对应于一个由n+1个0和n-1个1组成的排列。
反过来,任何一个由n+1个0和n-1个1组成的2n位二进制数,由于0的个数多2个,2n为偶数,故必在某一个奇数位上出现0的累计数超过1的累计数。同样在后面部分0和1互换,使之成为由n个0和n个1组成的2n位数,即n+1个0和n-1个1组成的2n位数必对应一个不符合要求的数。
因而不合要求的2n位数与n+1个0,n-1个1组成的排列一一对应。
显然,不符合要求的方案数为c(2n,n+1)。由此得出输出序列的总数目=c(2n,n)-c(2n,n+1)=c(2n,n)/(n+1)=h(n+1)。
类似问题 买票找零
有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈)

凸多边形三角划分

在一个凸多边形中,通过若干条互不相交的对角线,把这个多边形划分成了若干个三角形。任务是键盘上输入凸多边形的边数n,求不同划分的方案数f(n)。比如当n=6时,f(6)=14。[6]
分析
如果纯粹从f(4)=2,f(5)=5,f(6)=14,……,f(n)=n慢慢去归纳,恐怕很难找到问题的递推式,我们必须从一般情况出发去找规律。
因为凸多边形的任意一条边必定属于某一个三角形,所以我们以某一条边为基准,以这条边的两个顶点为起点P1和终点Pn(P即Point),将该凸多边形的顶点依序标记为P1、P2、……、Pn,再在该凸多边形中找任意一个不属于这两个点的顶点Pk(2<=k&lt;=n-1),来构成一个三角形,用这个三角形把一个凸多边形划分成两个凸多边形,其中一个凸多边形,是由P1,P2,……,Pk构成的凸k边形(顶点数即是边数),另一个凸多边形,是由Pk,Pk+1,……,Pn构成的凸n-k+1边形。
此时,我们若把Pk视为确定一点,那么根据乘法原理,f(n)的问题就等价于——凸k多边形的划分方案数乘以凸n-k+1多边形的划分方案数,即选择Pk这个顶点的f(n)=f(k)×f(n-k+1)。而k可以选2到n-1,所以再根据加法原理,将k取不同值的划分方案相加,得到的总方案数为:f(n)=f(2)f(n-2+1)+f(3)f(n-3+1)+……+f(n-1)f(2)。看到此处,再看看卡特兰数的递推式,答案不言而喻,即为f(n)=h(n-2) (n=2,3,4,……)。
最后,令f(2)=1,f(3)=1。
此处f(2)=1和f(3)=1的具体缘由须参考详尽的“卡特兰数”,也许可从凸四边形f(4)=f(2)f(3)+ f(3)f(2)=2×f(2)f(3)倒推,四边形的划分方案不用规律推导都可以知道是2,那么2×f(2)f(3)=2,则f(2)f(3)=1,又f(2)和f(3)若存在的话一定是整数,则f(2)=1,f(3)=1。(因为我没研究过卡特兰数的由来,此处仅作刘抟羽的臆测)。
类似问题
一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果她从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?
在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?

给定节点组成二叉树

给定N个节点,能构成多少种不同的二叉树?[7]
(能构成h(N)个)
(这个公式的下标是从h(0)=1开始的)

Catalan数的解法

Catalan数的组合公式为 Cn=C(2n,n) / (n+1);

此数的递归公式为 h(n ) = h(n-1)*(4*n-2) / (n+1)

/* 大数解

对于大数来说,就应该使用下面的大数算法。

使用的公式为:h(n) = h(n-1)*(4*n-2)/(n+1);

*/

// 0ms

#include<iostream>

using namespace std;

#define MAX 100

#define BASE 10000

void multiply(int a[],int Max,int b) //大数乘法,注意参数的传递

{

    int i,array=0;

    for (i = Max-1; i >= 0; i--)   

    {

        array += b * a[i];

        a[i] = array % BASE; // 数组每一位存放大数的四位数字

        array /= BASE;   

    }

}

void divide(int a[], int Max, int b) //模拟大数除法

{

    int i, div = 0;

    for (i = 0; i < Max; i++)   

    {

        div = div * BASE + a[i];

        a[i] = div / b;

        div %= b;

    }

}

int main()

{

    int a[101][MAX],i, n;

    memset(a[1],0,MAX*sizeof(int));

    for (i=2, a[1][MAX-1] = 1; i < 101; i++) // 高坐标存放大数低位

    {

        memcpy(a[i], a[i-1], MAX * sizeof(int));      //h[i] = h[i-1];

        multiply(a[i], MAX, 4 * i - 2);               //h[i] *= (4*i-2);

        divide(a[i], MAX, i + 1);                  //h[i] /= (i+1);

    }

    while (cin >> n)   

    {

        for (i = 0; i < MAX && a[n][i] == 0; i++); //去掉数组前为0的数字。

        cout << a[n][i++];             //输出第一个非0数

        for (; i < MAX; i++)   

        {

    printf("%04d",a[n][i]);       //输出后面的数,并每位都保持4位长度!(32767)

   }

        cout << endl;

    }

    return 0;

}

AC CODE 2:

//C(0) = 1 ; (n+2)*C(n+1) = (4n+2)*C(n); 也即是:h(n) = h(n-1) * (4 * n - 2)/(n+1);

// 0MS

#include<iostream>

using namespace std;

int a[101][101] = {0};

int main()

{

    int n,i,j,len,r,temp,t;

    int b[101];

    a[1][0] = 1; // 低坐标存放大数的低位

    len = 1;

    b[1] = 1;

    for (i = 2; i <= 100; i++)

    {

        t = i - 1;

        for (j=0;j<len;j++) // 模拟乘法,从低位开始

   {

    a[i][j] = a[i-1][j] * (4 * t + 2);

   }

        for (r = j = 0; j < len; j++) // 处理相乘结果

        {

            temp = a[i][j] + r;

            a[i][j] = temp % 10;

            r = temp / 10;

        }

        while (r) // 进位处理

        {

            a[i][len++] = r % 10;

            r /= 10;

        }

        for (j = len-1, r = 0; j >= 0; j--) // 模拟除法,从高位开始

        {

            temp = r * 10 + a[i][j];

            a[i][j] = temp / (t+2);

            r = temp % (t+2);

        }

        while (!a[i][len-1]) // 高位零处理

   {

    len--;

   }

        b[i] = len; // 记录结果的长度

    }

    while (cin >> n)

    {  

        for(j = b[n] - 1; j >= 0; j--)

   {

    printf("%d",a[n][j]);

   }

        printf("\n");

    }

    return 0;

Catalan数的基本公式就是上个部分所列出的那样,但是却有一些变形和具体的性质:

1、C_n = {2n/choose n} - {2n/choose n+1} /quad n/ge 0

这是根据原来的式子推导出来的,大概过程是这样的:C_n = /frac{1}{n+1}{2n/choose n} = {2n/choose n} -  /frac{n}{n+1}{2n/choose n} = {2n/choose n} -  {2n/choose n + 1}

2、C_0 = 1 /quad , /quad C_{n+1}=/frac{2(2n+1)}{n+2}C_n

这个递推式很容易可以从原来的式子中获得

3、/begin{displaymath}C_0 = 1 /quad , /quad C_{n+1}=/sum_{i=0}^{n}C_i/,C_{n-i}/quad n/ge 0/end{displaymath}

4、/begin{displaymath}C_n= /frac 1{n+1} /sum_{i=0}^n {n /choose i}^2/end{displaymath}

5、/begin{displaymath}C_n /sim /frac{4^n}{n^{/frac{3}{2}}/sqrt{/pi}}/end{displaymath}

这个是Catalan数的增长趋势。

详见:http://www.cnblogs.com/jackge/archive/2013/05/19/3086519.html

0 0
原创粉丝点击