信奥赛C++提高组csp-s之组合数学专题课:卡特兰数

一、数学原理
1. 定义
卡特兰数通常用 C n C_n Cn表示,其中 n 是非负整数。常见的定义方式有多种,其中一种是:
C n = 1 n + 1 ( 2 n n ) C_n = \frac{1}{n+1} \binom{2n}{n} Cn=n+11(n2n)
这个公式称为卡特兰数的闭式表达式。前几个卡特兰数为:
C 0 = 1 , C 1 = 1 , C 2 = 2 , C 3 = 5 , C 4 = 14 , C 5 = 42 , C 6 = 132 , ... C_0 = 1, \quad C_1 = 1, \quad C_2 = 2, \quad C_3 = 5, \quad C_4 = 14, \quad C_5 = 42, \quad C_6 = 132, \dots C0=1,C1=1,C2=2,C3=5,C4=14,C5=42,C6=132,...
卡特兰数也可以由递推关系定义:
C 0 = 1 , C n + 1 = ∑ i = 0 n C i C n − i ( n ≥ 0 ) C_0 = 1, \quad C_{n+1} = \sum_{i=0}^{n} C_i C_{n-i} \quad (n \ge 0) C0=1,Cn+1=∑i=0nCiCn−i(n≥0)
这个递推关系反映了卡特兰数的组合结构。
2. 递推关系
卡特兰数满足:
C 0 = 1 , C n = ∑ k = 0 n − 1 C k C n − 1 − k ( n ≥ 1 ) C_0 = 1, \quad C_n = \sum_{k=0}^{n-1} C_k C_{n-1-k} \quad (n \ge 1) C0=1,Cn=∑k=0n−1CkCn−1−k(n≥1)
这个递推关系可以通过多种组合解释得到。例如,考虑一个由 n 对括号组成的合法括号序列。设第一个左括号与其匹配的右括号之间有 k 对括号,那么这 k 对括号内部必须是一个合法序列,而剩余 n-1-k 对括号在外部,也是合法序列。因此,总数满足上述递推。
二、卡特兰数在计数问题中的应用
5.1 括号匹配
n 对括号的合法匹配序列数。例如 n=3 时有 5 种:((())), (()()), (())(), ()(()), ()()()。证明:如上所述,利用第一个左括号与其匹配右括号的位置进行递归。
5.2 二叉树计数
n 个节点(或 n+1 个叶子)的不同二叉树的个数。这里二叉树指每个节点有0或2个子节点的满二叉树(或指每个节点最多有两个子节点的二叉树,视定义而定)。实际上,n 个节点的不同二叉树(每个节点有左、右子树,可能为空)的个数也是卡特兰数。证明:设根节点,左子树有 k 个节点,右子树有 n-1-k 个节点,则总数满足递推。
5.3 多边形三角剖分
凸 n+2 边形通过添加不相交的对角线将其分割成三角形的方法数。例如三角形(n=1)只有一种,四边形(n=2)有两种。证明:选定一条边,其对应的顶点将多边形分成两个小多边形,递归得到递推。
5.4 Dyck 路径
从 (0,0) 到 (2n,0) 的格点路径,每次向上 (1,1) 或向下 (1,-1),且始终不穿过 x 轴(即路径上的点纵坐标非负)。这样的路径数就是 (C_n)。证明可用反射法或递推。
5.5 栈排序
一个栈的入栈序列为 1,2,...,n,则不同的出栈序列个数为 C n C_n Cn。这是因为入栈和出栈可以对应括号匹配。
5.6 其他
还有如:n 个节点的不同形状的二叉搜索树个数;n+1 个矩阵连乘的不同结合方式;圆周上 2n 个点连成不相交弦的方法数等。
三、编程案例:[NOIP2003 普及组] 栈
题目背景
栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表。
栈有两种最重要的操作,即 pop(从栈顶弹出一个元素)和 push(将一个元素进栈)。
栈的重要性不言自明,任何一门数据结构的课程都会介绍栈。宁宁同学在复习栈的基本概念时,想到了一个书上没有讲过的问题,而他自己无法给出答案,所以需要你的帮忙。
题目描述

宁宁考虑的是这样一个问题:一个操作数序列, 1 , 2 , ... , n 1,2,\ldots ,n 1,2,...,n(图示为 1 到 3 的情况),栈 A 的深度大于 n n n。
现在可以进行两种操作,
- 将一个数,从操作数序列的头端移到栈的头端(对应数据结构栈的 push 操作)
- 将一个数,从栈的头端移到输出序列的尾端(对应数据结构栈的 pop 操作)
使用这两种操作,由一个操作数序列就可以得到一系列的输出序列,下图所示为由 1 2 3 生成序列 2 3 1 的过程。

(原始状态如上图所示)
你的程序将对给定的 n n n,计算并输出由操作数序列 1 , 2 , ... , n 1,2,\ldots,n 1,2,...,n 经过操作可能得到的输出序列的总数。
输入格式
输入文件只含一个整数 n n n( 1 ≤ n ≤ 18 1 \leq n \leq 18 1≤n≤18)。
输出格式
输出文件只有一行,即可能输出序列的总数目。
输入输出样例 1
输入 1
3
输出 1
5
思路分析
题目要求输入一个整数n,输出序列1,2,...,n经过一个栈后可能得到的输出序列的总数。这正是卡特兰数的经典应用场景。
给定n个元素,按照 1,2,...,n 的顺序入栈,问可能的出栈序列有多少种?答案就是第n个卡特兰数 C n C_n Cn。
例如,当n=3时,有5种出栈序列:321, 231, 213, 132, 123。
由于题目中n≤18,结果远小于 2 31 − 1 2^{31}-1 231−1,我们可以直接使用long long和递推公式进行计算 。
代码实现
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 20;
ll f[N]; // f[i] 存储第 i 个卡特兰数
int main() {
int n;
scanf("%d", &n);
// 初始化
f[0] = 1;
f[1] = 1; // 卡特兰数的第0项和第1项都是1
// 递推计算卡特兰数
for (int i = 2; i <= n; i++) {
// 根据公式 f[n] = sum_{k=0}^{n-1} f[k] * f[n-1-k]
for (int k = 0; k < i; k++) {
f[i] += f[k] * f[i - 1 - k];
}
}
printf("%lld\n", f[n]);
return 0;
}
功能分析
- 核心逻辑 :卡特兰数的递推公式。循环变量
i代表当前计算的卡特兰数f[i]。内层循环k从0遍历到i-1,将乘积f[k] * f[i-1-k]累加到f[i]上。 - 时间复杂度:O(n²),对于n≤18来说绰绰有余。
- 正确性:这个递推公式在数学上等价于所有卡特兰数的定义,因此可以准确求出n=18以内的所有值 。
更多系列知识,请查看专栏:《信奥赛C++提高组csp-s知识详解及案例实践》:
https://blog.csdn.net/weixin_66461496/category_13113932.html
各种学习资料,助力大家一站式学习和提升!!!
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}
1、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html
2、csp信奥赛冲刺一等奖有效刷题题解:
CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新)
https://blog.csdn.net/weixin_66461496/category_13125089.html
3、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html
4、csp/信奥赛C++,完整信奥赛系列课程(永久学习):
https://edu.csdn.net/lecturer/7901 点击跳转
· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}