事情是这样的,某天在洛谷题库乱翻题写,突然看到一道普及-的东西【P1044 栈】,觉得啊,可以娱乐一下身心,就点开了,自己水了一下之后,照例围观题解,发现了这是“卡特兰数”的裸题【在此之前也听说过似乎】,然后一脸憧憬地去百度,get以下内容:
Catalan数是用来解决以下类似问题的:
n个1和n个0组成一2n位的2进制数,要求从左到右扫描,1的个数不小于0的个数,试求满足这条件的数有多少?
那么,卡特兰数怎么求呢?
Catalan数的定义令h(1)=1,Catalan数满足递归式:h(n) = h(1)*h(n-1) + h(2)*h(n-2) + ... + h(n-1)h(1),n>=2
该递推关系的解为:h(n) = C(2n-2,n-1)/n,n=1,2,3,...(其中C(2n-2,n-1)表示2n-2个中取n-1个的组合数)
以下提供几种求解方式:
1.h(n)=(4n-2)/(n+1)*h(n-1) (n>1) h(0)=h(1)=1
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n;
double dp[20];
int main()
{
scanf("%d",&n);
dp[0]=dp[1]=1;//卡特兰数列的前两项(和斐波拉契数列没差)
for(int i=2;i<=n;i++)
{
double temp=(4*i-2.0)/(i+1.0);//会有小数部分
dp[i]=temp*dp[i-1];//卡特兰数列递推式
}
printf("%.lf\n",dp[n]);
return 0;
}
2.
#include<iostream>
#include<cstdio>
#include<cstring>
int f[30];
using namespace std;
int main(){
int n;
scanf("%d",&n);
f[0]=f[1]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<i;j++){
f[i]+=f[j]*f[i-j-1];
}
}
printf("%d\n",f[n]);
return 0;
}
其实重点应该是应用。。。:【from Wiki】
应用
组合数学中有非常多.的组合结构可以用卡塔兰数来计数。在Richard P. Stanley的Enumerative Combinatorics: Volume 2一书的习题中包括了66个相异的可由卡塔兰数表达的组合结构。以下用Cn=3和Cn=4举若干例:
- Cn表示长度2n的dyck word的个数。Dyck word是一个有n个X和n个Y组成的字串,且所有的部分字串皆满足X的个数大于等于Y的个数。以下为长度为6的dyck words:
- 将上例的X换成左括号,Y换成右括号,Cn表示所有包含n组括号的合法运算式的个数:
- Cn表示有n+1个叶子的二叉树的个数。
- Cn表示所有不同构的含n个分枝结点的满二叉树的个数。(一个有根二叉树是满的当且仅当每个结点都有两个子树或没有子树。)
证明:
令1表示进栈,0表示出栈,则可转化为求一个2n位、含n个1、n个0的二进制数,满足从左往右扫描到任意一位时,经过的0数不多于1数。显然含n个1、n个0的2n位二进制数共有个,下面考虑不满足要求的数目.
考虑一个含n个1、n个0的2n位二进制数,扫描到第2m+1位上时有m+1个0和m个1(容易证明一定存在这样的情况),则后面的0-1排列中必有n-m个1和n-m-1个0。将2m+2及其以后的部分0变成1、1变成0,则对应一个n+1个0和n-1个1的二进制数。反之亦然(相似的思路证明两者一一对应)。
从而。证毕。
- Cn表示所有在n × n格点中不越过对角线的单调路径的个数。一个单调路径从格点左下角出发,在格点右上角结束,每一步均为向上或向右。计算这种路径的个数等价于计算Dyck word的个数: X代表“向右”,Y代表“向上”。下图为n = 4的情况:
-
- Cn表示通过连结顶点而将n + 2边的凸多边形分成三角形的方法个数。下图中为n = 4的情况:
- Cn表示对{1, ..., n}依序进出栈的置换个数。一个置换w是依序进出栈的当S(w) = (1, ..., n), 其中S(w)递归定义如下:令w = unv,其中n为w的最大元素,u和v为更短的数列;再令S(w) =S(u)S(v)n,其中S为所有含一个元素的数列的单位元。
- Cn表示集合{1, ..., n}的不交叉划分的个数.
- Cn表示用n个长方形填充一个高度为n的阶梯状图形的方法个数。下图为 n = 4的情况:
所以真的是裸题了。。。