Catalan数应用整理

应用一:

codevs 3112 二叉树计数

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 黄金 Gold
 
题目描述 Description

一个有n个结点的二叉树总共有多少种形态

输入描述 Input Description

读入一个正整数n

输出描述 Output Description

输出一个正整数表示答案

样例输入 Sample Input

6

样例输出 Sample Output

132

数据范围及提示 Data Size & Hint

1<=n<=20

 1 #define N 25
 2 #include<cstdio>
 3 #include<iostream>
 4 using namespace std;
 5 long long f[N];
 6 int main()
 7 {
 8     int n;
 9     scanf("%d",&n);
10     f[0]=1;f[1]=1;
11     for(int i=2;i<=n;++i)
12       for(int k=0;k<i;++k)
13       f[i]+=f[k]*f[i-1-k];
14     printf("%d\n",f[n]);
15     return 0;
16 }

下面解释:为什么n个节点的二叉树的形态数目是Catalan数?

 1 /*
 2 先考虑只有一个节点的情形,设此时的形态有f(1)种,那么很明显f(1)=1
 3 
 4 如果有两个节点呢?我们很自然想到,应该在f(1)的基础上考虑递推关系。那么,如果固定一个节点后,有两种情况,一是左子树还剩一个节点,此刻类型数量为f(1),第二种情况是右子树生一个节点,此刻类型数量为f(1),固有f(2) = f(1) + f(1)
 5 
 6 如果有三个节点呢?我们需要考虑固定两个节点的情况么?当然不行,为什么?
 7 
 8 因为当节点数量大于等于2时,无论你如何固定,其形态必然有多种,而在这多种基础之上你如何安排后续剩下的节点呢?所以必须挑出这个误区。
 9 
10 回到二叉树的定义,二叉树本质上就是一个递归的形式,左子树,右子树,根节点。所以根节点应该不变,需要递归处理的是左右子树。
11 
12 也就是说,还是考虑固定一个节点,即根节点。好的,按照这个思路,还剩2个节点,那么左右子树的分布情况为2=0+2=1+1=2+0。
13 
14 所以有3个节点时,递归形式为f(3)=f(2) + f(1)*f(1) + f(2). (注意这里的乘法,因为左右子树一起组成整棵树,根据排列组合里面的乘法原理即可得出)
15 
16 那么有n个节点呢?我们固定一个节点,那么左右子树的分布情况为n-1=n-1 + 0 = n-2 + 1 = ... = 1 + n-2 = 0 + n-1
17 
18 OK。递归表达式出来了f(n) = f(n-1) + f(n-2)f(1) + f(n-3)f(2) + ... + f(1)f(n-2) + f(n-1)
19 
20  
21 
22 观察一下这个表达式,嗯,和我们之前见过的递归表达有一点区别,递推层级为n的时候,更多的是考虑前一步(n-1),或者前两步(n-1)和(n-2)。
23 
24 但是这里却考虑到所有的情况,即1到n-1。
25 
26 最后说明一下,这个表达式有一个学名,叫做Catalan数。上面我们没有定义f(0)。如果把f(0)也考虑进去,显然没有节点也只有一种情况,即f(0)=1
27 
28 标准表达式为f(n) = f(n-1)f(0) + f(n-2)f(1) + f(n-3)f(2) + ... + f(1)f(n-2) + f(n-1)f(0)
29 
30 前几个数为1,1,2,5,14,42,132。
31 */

应用二:

codevs 3134 Circle 

 时间限制: 1 s
 空间限制: 32000 KB
 题目等级 : 黄金 Gold
 
题目描述 Description

在一个圆上,有2*K个不同的结点,我们以这些点为端点,连K条线段,使得每个结点都恰好用一次。在满足这些线段将圆分成最少部分的前提下,请计算有多少种连线的方法

输入描述 Input Description

仅一行,一个整数K(1<=K<=30)

输出描述 Output Description

两个用空格隔开的数,后者为最少将圆分成几块,前者为在此前提下连线的方案数

样例输入 Sample Input

2

样例输出 Sample Output

2 3

数据范围及提示 Data Size & Hint
 1 #include<cstdio>
 2 int n;
 3 long long f;
 4 int main()
 5 {
 6     scanf("%d",&n);
 7     f=1;
 8     for(int i=2;i<=n;++i)
 9     f=f*(4*i-2)/(i+1);
10     printf("%lld %d",f,n+1);
11 /*最少的划分部分是n条线段都不相交*/
12     return 0;
13 }

 

posted @ 2016-11-15 14:51  csgc0131123  阅读(912)  评论(0编辑  收藏  举报