当前位置: 首页 > news >正文

C++卡特兰数讲解

前情提要,参考资料:卡特兰数 - OI Wiki

一、定义

        卡特兰数(Catalan number)是一个在组合数学中经常出现的数列,应用范围很广,例如括号匹配问题、出栈顺序问题、多边形三角剖分问题等。在 C++ 中,可以使用多种方法来计算卡特兰数,包括动态规划和递归等。​


 

二、推导

        1.速记20项:

C0 = 1,         
C1 = 1,         C2 = 2,          C3 = 5,          C4 = 14,          C5 = 42,
C6 = 132,       C7 = 429,        C8 = 1430,       C9 = 4862,        C10 = 16796,
C11 = 58786,    C12 = 208012,    C13 = 742900,    C14 = 2674440,    C15 = 9694845,
C16 = 35357670, C17 = 129644790, C18 = 477638700, C19 = 1767263190, C20 = 6564120420, ...

        2.通项公式:C_n= for(1-n)\frac{4k-2}{k+1}C_{n-1}(但其适用范围约1~100)

//算法:Catalan数
//时间复杂度:O(n)
#include <iostream>
#include <algorithm>
#define int unsigned long long
using namespace std;
const int N = 1e5+9;
int n,f[N];signed main(void){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin >> n;f[0] = 1;for(int i = 1; i <= n; ++i)  f[i] = f[i-1]*(4*i-2)/(i+1);cout << f[n] << "\n";return 0;
}

        3.二叉树绘图

        说明: 3-0 中 左边的数为向左拓几个节点,右边则向右拓节点。那 3 即向左拓 3 个节点,0则不拓。见图

        4.括号序列:

                注:请对照以上二叉树观察

                说明:区域内单独一个括号为向左拓点,标注在括号中的括号为向右拓。若有不清请自行思考。

1. ()()()
2. (())()
3. ()(())
4. (()())
5. ((()))

        5.节点式:(或动态规划法)

                仍是以以上二叉树为例,n 个节点共 n-1 种搭配方式,一共 C_n = \sum_{k=0}^{n-1} C_kC_{n-1-k} 种方案。

                ed:

                C_3 = C_2C_0+C_1C_1+C_0C_2 = 2*1+1*1+1*2=5

                C_4=C_3C_0+C_2C_1+C_1C_2+C_0C_3=5*1+2*1+1*2+1*5=14

        Code ed:

//算法:卡特兰数
//时间复杂度:O(n^2) 
#include <iostream>
#include <vector>
#include <algorithm>
#define int unsigned long long
using namespace std;int Catalan(int n){//递推法(动态规划)计算第n个卡特兰数vector<int> f(n + 1, 0);f[0] = 1;for(int i = 1; i <= n; ++i){for(int j = 0; j < i; ++j){f[i] += f[j] * f[i - j - 1];}}return f[n];
}signed main(void){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);int n; cin >> n;cout << Catalan(n) << "\n";return 0;
}

        6.二项式系数法:

        直接公式:C_n=\frac{1}{n+1}*C(2n,n) 

        Code ed:

//算法:卡特兰数
//时间复杂度:O(n) 
#include <iostream>
#include <algorithm>
#define int unsigned long long
using namespace std;int binomialCoeff(int n, int k){if(k > n-k)  k = n-k;int res = 1;for(int i = 0; i < k; ++i){res *= (n - i);res /= (i + 1);}return res;
}int Catalan(int n){int C = binomialCoeff(2*n, n);return C / (n+1);
}signed main(void){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);int n; cin >> n;cout << Catalan(n) << "\n";return 0;
}

        注:该算法当 n≥20 时可能溢出 unsigned long long 的范围

               且必须使用整数运算除法必须在最后一步进行。

 


三、应用 

        范围较为广泛:

  • 括号序列: 合法的括号序列数量。
  • 二叉树: 二叉搜索树的数量。
  • 出栈序列: 出栈后1~n多少排列方式
  • Dyck 语言:在形式语言理论中,卡特兰数与 Dyck 语言中的字符串数量有关。
  • 山脉数量:由 nn 个“上”和 nn 个“下”组成的山脉序列的数量。
  • 路径问题:在网格中,从左下角到右上角的非交叉路径的数量。

统一模板:

//算法:卡特兰数
//时间复杂度:O(n^2) 
#include <iostream>
#include <vector>
#include <algorithm>
#define int unsigned long long
using namespace std;int Catalan(int n){vector<int> f(n + 1, 0);f[0] = 1;for(int i = 1; i <= n; ++i){for(int j = 0; j < i; ++j){f[i] += f[j] * f[i - j - 1];}}return f[n];
}signed main(void){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);int n; cin >> n;cout << Catalan(n) << "\n";return 0;
}
/*
//算法:Catalan数
//时间复杂度:O(n)
#include <iostream>
#include <algorithm>
#define int unsigned long long
using namespace std;
const int N = 1e5+9;
int n,f[N];signed main(void){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);cin >> n;f[0] = 1;for(int i = 1; i <= n; ++i)  f[i] = f[i-1]*(4*i-2)/(i+1);cout << f[n] << "\n";return 0;
}
*/
/*
//算法:卡特兰数
//时间复杂度:O(n^2) 
#include <iostream>
#include <algorithm>
#define int unsigned long long
using namespace std;int binomialCoeff(int n, int k){if(k > n-k)  k = n-k;int res = 1;for(int i = 0; i < k; ++i){res *= (n - i);res /= (i + 1);}return res;
}int Catalan(int n){int C = binomialCoeff(2*n, n);return C / (n+1);
}signed main(void){ios::sync_with_stdio(false);cin.tie(nullptr),cout.tie(nullptr);int n; cin >> n;cout << Catalan(n) << "\n";return 0;
}
*/

 

四、典例

        1.出栈顺序:

                ed:P1044 [NOIP 2003 普及组] 栈 - 洛谷

                思路: 需利用乘法原理加法原理。1~n分为 1~k-1,序列个数为k-1;另外的是k+1~n,序列个数为n-k。通过递归的、得f(n)=f(k-1)*f(n-k)。接着往下推即可得到形似于C_n = \sum_{k=0}^{n-1} C_kC_{n-1-k} 的公式。

        2.二叉树计数

                类似于以上讲解,作图推导即可。

        3.阶梯问题

                需要用到动态规划。

               同上: C_n = \sum_{k=0}^{n-1} C_kC_{n-1-k}

        


 

五、总结        

        性质:卡特兰数随着 nn 的增加而迅速增长。

                   卡特兰数在组合数学中具有许多有趣的性质,例如它们是对称的,即 Cn=C2n−nCn​=C2n−n​。

        计算:在实际编程中,计算卡特兰数时需要注意整数溢出的问题,特别是当 nn 较大时。可以使用大数库或模运算来处理大数。

        省流:卡特兰数是一个强大的工具,用于解决各种组合问题。它们在计算机科学、数学和物理学中都有广泛的应用。理解卡特兰数的定义、递推公式和应用可以帮助你在解决相关问题时更加得心应手。当然,速记20项能记住就记住哦!


 

六、习题

        P1044 [NOIP 2003 普及组] 栈 - 洛谷

        P2532 [AHOI2012] 树屋阶梯 - 洛谷

        P1722 矩阵 II - 洛谷

        P1976 鸡蛋饼 - 洛谷

http://www.xdnf.cn/news/350407.html

相关文章:

  • Java 显式锁与 Condition 的使用详解
  • Android MVC架构的现代化改造:构建清晰单向数据流
  • AI搜索的未来:技术纵深发展与关键突破路径
  • Kubernetes 手动部署 Prometheus 学习计划
  • 【计算机网路】--tcp四次挥手关闭连接
  • pm2 list查询服务时如何通过name或者namespace进行区分
  • 文本文件的定义
  • CTF杂项入门(BUUCTF-Misc第一页)
  • Python机器学习中的字典列表特征提取
  • 基于vue3+QuillEditor的深度定制
  • [数据库之十四] 数据库索引之位图索引
  • 最短路径-Dijkstra及其堆优化版本
  • 指纹浏览器技术解析:从原理到实战的多账号管理解决方案
  • 数据清洗(ETL/ELT)原理与工具选择指南:企业数字化转型的核心引擎
  • 常用 svg ICON
  • FreeRTOS如何检测内存泄漏
  • Linux操作系统中的通知机制 - 监控文件事件 inotify
  • 印度股票市场API对接文档
  • 麒麟信安举办特种行业核心代理商中级技术认证培训班
  • 【计网】TCP/IP四层模型(一)
  • [硬件电路-18]:MCU - LPC1765FBD100是恩智浦(NXP)半导体推出的一款基于ARM Cortex-M3内核的高性能32位微控制器
  • 如果说开启的TIM3定时器有ccr1,ccr2,ccr3,我想要关闭ccr2的PWM输出,怎么通过代码实现
  • AI优化高频PCB信号完整性:猎板PCB的技术突破与应用实践
  • 多环串级PID
  • 主场景 工具栏 植物卡牌的渲染
  • 从“看不见”到“一目了然”:网络流量分析与监控大屏
  • 手撕基于AMQP协议的简易消息队列-6(服务端模块的编写)
  • 云计算运维
  • vue实现半圆转盘旋转(门户网页上)
  • 企业级UI测试的“双保险”:TestComplete的智能对象识别与详细报告功能