在编程之美上又看到卡特兰数的题目,所以就把这类题目做个总结。
至此,已经遇到过得卡特兰数的题目有:
1、12个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?
解答:12个高矮不同,则可以编号1 2 3 4 5 6 7 8 9 10 11 12 ,现在0代表第一排,1代表第二排
000000111111是一个合法序列,010101010101也是一个合法序列,可以看到对于每个1,只要前面的0的个数大于等于1的个数就是一个合法序列,011000001111,由于第2个1,前面0只有1个,1有两个,不符合。
换个角度,1号1的位置大于1号0的位置,2号1的位置大于2号0的位置。。。。,6号1的位置大于6号0的位置
2、用S表示入栈,X表示出栈,那么合法的序列有多少个(S的个数为n)
解答:出栈X前必须要有大于等于X个数的S操作,SSSSXXXX合法,SXSXSXSX合法,但是SXXSSXSX就不合法,和上题一样,1号X的位置要大于1号S的位置,2号X的位置要大于2号S的位置,以此类推
3、括号化问题。矩阵链乘: P=A1×A2×A3×……×An,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?
解答:每个右括号前面要有大于等于左括号的个数,和前面题目一样,1号右括号位置大于1号左括号,2号右括号位置大于2号左括号。。。
4、假设有2N个人在排队买票,其中有N个人手持50元的钞票,另外有N个人手持100元的钞票,假设开始售票时,售票处没有零钱,问这2N个人有多少种排队方式,不至使售票处出现找不开钱的局面?
解答:每个持100的前面必须要有大于等于持50的人的个数,即1号100必须在1号50右边,2号100必须在2号50右边。。。直到n号
5、将多边行划分为三角形问题。将一个凸多边形区域分成三角形区域(划分线不交叉)的方法数?
类似:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?
6、给顶节点组成二叉树的问题。
给定N个节点,能构成多少种形状不同的二叉树?
这六道题目看似不同,实则是一样的, 即对每种合法排列来说,里面的每个元素都必须满足某个条件(常常是前面的某个个数要多于自己类型元素的个数)
解答:(一定是二叉树!先取一个点作为顶点,然后左边依次可以取0至N-1个相对应的,右边是N-1到0个,两两配对相乘,就是h(0)*h(n-1) + h(2)*h(n-2) + ...... + h(n-1)h(0)=h(n)) (能构成h(N)个)
卡特兰数
原理:
令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)=((4*n-6)/(n))*h(n-1);
该递推关系的解为:
h(n)=C(2n,n)/(n + 1) (n=1,2,3,...)
talk is cheap,show the code:
#include <iostream>
using namespace std;
int bit_cnt(int n)//计算非负整数中二进制数中1的个数
{
int result = 0;
for (; n; n &= n-1, ++result);
return result;
}
int main(void)
{
int F[6], B[6];//F代表前排 B代表后排
int i,j,k,state,ok,ans = 0;
for (state = 0; state < (1 << 12); ++state)
{
if (bit_cnt(state) == 6)
{
i = j = 0;
for (int k = 0; k < 12; ++k)//确定每个0和1所在的位置
{
if(state&(1<<k))
F[i++] = k;
else
B[j++] = k;
}
ok = 1;
for (k = 0; k < 6; ++k)//筛选满足条件的排列
{
if (B[k] < F[k])
{
ok = 0;
break;
}
}
ans += ok;
}
}
cout << ans << endl;
return 0;
}