zoukankan      html  css  js  c++  java
  • 卡特兰数

    网上找的大神的总结。中间很多图片省略掉了。。。以后补上。

    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元出栈)

    更一般情况,当有m个人有5元,n个人有n个10元时,总方案数:

    合法序列数量 = 序列总数量 - 不合法序列的总量
    序列总数可以这样计算m+n 个位置中, 选择 n 个位置出来填上 1, 所以是 C(m+n, n)
    不合法序列的数量就是: m+n 个位置中, 选择 m+1 个位置出来填上 1 所以是 C(m+n, m+1)
    然后每个人都是不一样的,所以需要全排列 m! * n!

    F(N)= ( C(m+n,n) - C(m+n,m+1) ) * m! * n! =(M+N)! * (M-N+1)/(M+1)

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

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

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

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

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

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

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

      (能构成h(N)个)

    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(" ");

        }

        return 0;

    《编程之美》中提到了“买票找零”问题,查阅了下资料,此问题和卡特兰数 Cn有关,其定义如下:

     

    卡特兰数真是一个神奇的数字,很多组合问题的数量都和它有关系,例如:

    • Cn= 长度为 2n的 Dyck words的数量。 Dyck words是由 n个 X和 n个 Y组成的字符串,并且从左往右数, Y的数量不超过 X,例如长度为 6的 Dyck words为:

    XXXYYY XYXXYY XYXYXY XXYYXY XXYXYY

    • Cn= n对括号正确匹配组成的字符串数,例如 3对括号能够组成:

    ((())) ()(()) ()()() (())() (()())

    • Cn= n+1个数相乘,所有的括号方案数。例如, 4个数相乘的括号方案为:

     

    ((ab)c)d (a(bc))d (ab)(cd) a((bc)d) a(b(cd))

    • Cn= 拥有 n+1 个叶子节点的二叉树的数量。例如 4个叶子节点的所有二叉树形态:

     

    • Cn=n*n的方格地图中,从一个角到另外一个角,不跨越对角线的路径数,例如, 4×4方格地图中的路径有:

     

    • Cn= n+2条边的多边形,能被分割成三角形的方案数,例如 6边型的分割方案有:

     

    • Cn= 圆桌周围有 2n个人,他们两两握手,但没有交叉的方案数。

    在《Enumerative Combinatorics》一书中,竟然提到了多达 66种组合问题和卡特兰数有关。

     

    分析

        “卡特兰数”除了可以使用公式计算,也可以采用“分级排列法”来求解。以 n对括弧的合法匹配为例,对于一个序列 (()而言,有两个左括弧,和一个右括弧,可以看成“抵消了一对括弧,还剩下一个左括弧等待抵消”,那么说明还可以在末尾增加一个右括弧,或者一个左括弧,没有左括弧剩余的时候,不能添加右括弧。

        由此,问题可以理解为,总共 2n个括弧,求 1~2n级的情况,第 i 级保存所有剩余 i 个左括号的排列方案数。 1~8级的计算过程如下表:

     

        计算过程解释如下: 1级:只能放 1个“(”; 2级:可以在一级末尾增加一个“)”或者一个“ (”

    以后每级计算时,如果遇到剩余 n>0个“(”的方案,可以在末尾增加一个“ (”或者“ )”进入下一级;遇到剩余 n=0个“(”的方案,可以在末尾增加一个“ (”进入下一级。

    奇数级只能包含剩余奇数个“(”的排列方案

    偶数级只能包含剩余偶数个“(”的排列方案

    从表中可以看出,灰色部分可以不用计算。

    解法

    关键代码为:

    double Catalan(int n) { if (n == 0) return 1; for (int i = 2; i <= 2 * n; i++) { var m = i <= n ? i : 2 * n + 1 - i; for (int j = (i - 1) & 1; j <= m; j += 2) { if (j > 0) arr[j - 1] += arr[j]; if (j < n) arr[j + 1] += arr[j]; arr[j] = 0; } } return arr[0]; }

     

    其中:

    n为 Cn中的 n;

    arr = new double[n + 1];//arr[i]代表有 k个括弧的时候,剩余 "("个数为 i的排列方案个数 arr[1] = 1;

     

    讨论

    算法复杂度为  = O(n^2),空间复杂度为 O(n+1)。相对于利用公式计算而言,此方法的优势在于——没有乘除法,只有加法。

  • 相关阅读:
    TextBox 只有下划线
    can't find web control library(web控件库)
    DropDownListSalesAC”有一个无效 SelectedValue,因为它不在项目列表中。
    IDE、SATA、SCSI、SAS、FC、SSD 硬盘类型
    如何打印1px表格
    CSS控制打印 分页
    Virtual Server could not open its emulated Ethernet switch driver. To fix this problem, reenable the Virtual Server Emulated Et
    Xml中SelectSingleNode方法中的xpath用法
    热带水果莫入冰箱?水果存放冰箱大法
    探索Asp.net的Postback机制
  • 原文地址:https://www.cnblogs.com/acm-jing/p/4330846.html
Copyright © 2011-2022 走看看