『笔记』数学数论(七)

\[\Huge{(七)组合数学之 \color{#3086B1}{卡特兰数}} \]

简介

卡特兰数(Catalan),又称明安图数,是组合数学中一个常出现于各种计数问题的数列。

对应序列为:

\(1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, \cdots\)

满足

\[M_{n+1} = M_0M_n + M_1M_{n-1} + \cdots + M_nM_0 \]

其中 \(C_0=1,C_1=1\)

性质

注:为避免符号重复本文所有公式将以 \(M_n\) 表示卡特兰数,即明安图数的拼音首字母。

符合通项公式:

\[M_{n}=C_{2 n}^{n}-C_{2 n}^{n+1}=\frac{C_{2 n}^{n}}{n+1}\left(n \geq 2, n \in N _{+}\right) \]

那么可以推出如下公式:

\[\begin{aligned} M_{n+1} &=\frac{M_{n}(4 n-2)}{n+2} & \\ M_{n} &=\left\{ \begin{array}{ll} \sum_{i=1}^{n} M_{i-1} M_{n-i} & n \geq 2, n \in N _{+} \\ 1 & n=0,1 \end{array}\right.\\ M_{n} &= \begin{bmatrix} 2 n \\ n \end{bmatrix}- \begin{bmatrix} 2 n \\ n-1 \end{bmatrix} \end{aligned} \]

基本原理

先来看一个问题:

对于 \(n\) 对括号,求共有多少种合法的匹配方案数。

其中括号的合法匹配方式为:一个左括号对应一个右括号,且左括号必须要在右括号前面出现。

这里我们用 +1 表示 “ \((\) ” ,用 -1 表示 “ \()\) ”。

那么显然,对于一组合法的括号序列,该序列的前缀和必然大于或等于 \(0\) ;相反,若该括号序列不合法,其前缀和必然存在小于 \(0\) 的情况。

首先给出一个序列:

()(())()

该序列可以表示为

+1 -1 +1 +1 -1 -1 +1 -1

显然这是一个非法序列。

对于像这样的一个非法序列,找到第一个前缀和小于 \(0\) 的括号,并对该前缀中的每一个数进行取反。

上述例子便可以得到

-1 +1 +1 +1 -1 +1 +1 -1

此时该序列中共有 \(3+1+1=5\)+1\(4-1=3\)-1 。不难看出,第一个小于 \(0\) 的前缀和必定为 \(-1\) ,即 \(-1\)\(+1\) 多一个,取反后则 \(-1\)\(+1\) 少一个。这样总体上看,由于二者数量原本相同,所以 \(+1\) 必定变为 \(n+1\) 个,\(-1\) 则变为 \(n-1\) 个。

由此,该结论可以推广:

对于 \(n\) 对括号的每种非法匹配序列 \(A\),一定会有一个含有 \(n+1\)\(+1\)\(n-1\)\(-1\) 的序列 \(B\) 与其一一对应。

而序列 \(B\) 的数量可以通过 \(C_{2n}^{n+1}\) 计算,即非法序列的数量为 \(C_{2n}^{n+1}\)

而序列的总数量为 \(C_{2n}^n\) (从 \(2n\) 个位置中选择 \(n\) 个位置放左括号,不考虑先后顺序),则合法的匹配序列数量为:

\[C_{2 n}^{n}-C_{2 n}^{n+1}=\cfrac{C_{2 n}^{n}}{n+1} \]

由此便推导出了卡特兰数的通项公式。

应用

P1044 [NOIP2003 普及组] 栈

直接由基本原理可以推出:

公式 1

\[H_n=\cfrac{H_{n−1} \times 4 \times n−2}{n+1} \]

代码:

/*

Name: P1044 [NOIP2003 普及组] 栈

Solution: 


By Frather_

*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long

using namespace std;
/*==================================================快读*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定义变量*/
const int _ = 110;

int n;
int h[_];
/*=============================================自定义函数*/

/*=================================================主函数*/
signed main()
{
    h[0] = 1;
    h[1] = 1;
    n = read();
    for (int i = 2; i <= n; i++)
        h[i] += h[i - 1] * (4 * i - 2) / (i + 1);
    printf("%lld\n", h[n]);
    return 0;
}

公式 2

\[H_n=H_0 \times H_{n-1}+H_1 \times H_{n-2} + \cdots + H_{n-1} \times H_0 (n \geq 2) \]

代码:

/*

Name: P1044 [NOIP2003 普及组] 栈

Solution: 


By Frather_

*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long

using namespace std;
/*==================================================快读*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定义变量*/
const int _ = 110;

int n;
int h[_];
/*=============================================自定义函数*/

/*=================================================主函数*/
signed main()
{
    h[0] = 1;
    h[1] = 1;
    n = read();
    for (int i = 2; i <= n; i++)
        for (int j = 0; j < i; j++)
            h[i] += h[j] * h[i - j - 1];
    printf("%lld\n", h[n]);
    return 0;
}

公式 3

\[H_n =\cfrac{C_{2n}^n}{n+1}(n=0,1,2,\cdots) \]

其中

\[C_m^n = C_{m-1}^{n-1} + C_{m-1}^n \]

并且规定

\[C_n^0=1, C_n^n=1, C_0^0=1 \]

代码:

/*

Name: P1044 [NOIP2003 普及组] 栈

Solution: 


By Frather_

*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long

using namespace std;
/*==================================================快读*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定义变量*/
const int _ = 110;

int n;
int h[_ << 1][_];
/*=============================================自定义函数*/

/*=================================================主函数*/
signed main()
{
    n = read();
    for (int i = 1; i <= n << 1; i++)
    {
        h[i][0] = 1;
        h[i][i] = 1;
        for (int j = 1; j < i; j++)
        {
            h[i][j] = h[i - 1][j] + h[i - 1][j - 1];
        }
    }
    printf("%lld\n", h[n << 1][n] / (n + 1));
    return 0;
}

公式 4

\[H_n=C_{2n}^n−C_{2n}^{n−1}(n=0,1,2,\cdots) \]

代码:

/*

Name: P1044 [NOIP2003 普及组] 栈

Solution: 


By Frather_

*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long

using namespace std;
/*==================================================快读*/
inline int read()
{
    int X = 0, F = 1;
    char CH = getchar();
    while (CH < '0' || CH > '9')
    {
        if (CH == '-')
            F = -1;
        CH = getchar();
    }
    while (CH >= '0' && CH <= '9')
    {
        X = (X << 3) + (X << 1) + (CH ^ 48);
        CH = getchar();
    }
    return X * F;
}
/*===============================================定义变量*/
const int _ = 110;

int n;
int h[_ << 1][_];
/*=============================================自定义函数*/

/*=================================================主函数*/
signed main()
{
    n = read();
    for (int i = 1; i <= n << 1; i++)
    {
        h[i][0] = 1;
        h[i][i] = 1;
        for (int j = 1; j < i; j++)
            h[i][j] = h[i - 1][j] + h[i - 1][j - 1];
    }
    printf("%lld\n", h[n << 1][n] - h[n << 1][n - 1]);
    return 0;
}

如果还有其他公式欢迎联系博主!(博主最多推四个了。。)

posted @ 2021-04-25 22:02  Frather  阅读(193)  评论(6编辑  收藏  举报