Localhost8080

知行合一,自强不息

 

Catalan数——卡特兰数

【Catalan数——卡特兰数】

Catalan数是组合数学中一个常出现在各种计数问题中出现的数列。由以比利时的数学家欧仁·查理·卡塔兰 (1814–1894)命名。

  原理:

  令h(0)=1,h(1)=1,catalan数满足递归式: 

 h(n)= h(0)*h(n-1) + h(1)*h(n-2) + ... + h(n-1)h(0) (其中n>=2)

  该递推关系的解为:

  h(n)=C(2n,n)/(n + 1) (n=1,2,3,...)

  前几项为 (OEIS中的数列A000108): 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ...

二.Catalan数的典型应用:

1.括号化问题。矩阵链乘: P=A1×A2×A3×……×An,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?

2.将多边行划分为三角形问题。将一个凸多边形区域分成三角形区域(划分线不交叉)的方法数?

类似:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?

3.出栈次序问题。一个栈(无穷大)的进栈序列为1,2,3,..n,有多少个不同的出栈序列?

类似:有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈)

类似:一位大城市的律师在他住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果他从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?

分析:对于每一个数来说,必须进栈一次、出栈一次。我们把进栈设为状态‘1’,出栈设为状态‘0’。n个数的所有状态对应n个1和n个0组成的2n 位二进制数。由于等待入栈的操作数按照1‥n的顺序排列、入栈的操作数b大于等于出栈的操作数a(a≤b),因此输出序列的总数目=由左而右扫描由n个1 和n个0组成的2n位二进制数,1的累计数不小于0的累计数的方案种数。

在2n位二进制数中填入n个1的方案数为c(2n,n),不填1的其余n位自动填0。从中减去不符合要求(由左而右扫描,0的累计数大于1的累计数)的方案数即为所求。

不符合要求的数的特征是由左而右扫描时,必然在某一奇数位2m+1位上首先出现m+1个0的累计数和m个1的累计数,此后的2(n-m)-1位上有 n-m个 1和n-m-1个0。如若把后面这2(n-m)-1位上的0和1互换,使之成为n-m个0和n-m-1个1,结果得1个由n+1个0和n-1个1组成的 2n位数,即一个不合要求的数对应于一个由n+1个0和n-1个1组成的排列。

反过来,任何一个由n+1个0和n-1个1组成的2n位二进制数,由于0的个数多2个,2n为偶数,故必在某一个奇数位上出现0的累计数超过1的累 计数。同样在后面部分0和1互换,使之成为由n个0和n个1组成的2n位数,即n+1个0和n-1个1组成的2n位数必对应一个不符合要求的数。

因而不合要求的2n位数与n+1个0,n-1个1组成的排列一一对应。

显然,不符合要求的方案数为c(2n,n+1)。由此得出 输出序列的总数目=c(2n,n)-c(2n,n+1)=1/(n+1)*c(2n,n)。(这个公式的下标是从h(0)=1开始的)

1.括号化问题。

  矩阵链乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?(h(n)种)

2.出栈次序问题。

  一个栈(无穷大)的进栈序列为1,2,3,..n,有多少个不同的出栈序列?

  类似:

  (1)有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈)

  (2)在圆上选择2n个点,将这些点成对连接起来,使得所得到的n条线段不相交的方法数。

3.将多边行划分为三角形问题。

  将一个凸多边形区域分成三角形区域的方法数?

  类似:一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果她

  从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?

  类似:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?

4.给顶节点组成二叉树的问题。

  给定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)个)

以前写过的一个大整数catlan数计算程序:

#include <iostream>
#include 
<string>
#include 
<iomanip>
#include 
<memory.h>
const long MaxLength=100;//数字最大位数是MaxLength *4
using namespace std;
class BigInt
{
public:
 
long *a;
 
long length;
 
long max;
 BigInt()
//构造函数
 {
   a 
= new long [MaxLength];
   memset(a,
0,sizeof(long)*MaxLength);
   length 
= 1;
   max 
= MaxLength;
 }
 BigInt(
const BigInt&t)
 {
   
long *= new long [t.length];
   
for(int i=0;i<t.length;i++)
    a[i] 
= t.a[i];
   length 
= t.length;
   max 
= t.max;

 }
 
~BigInt()//析构函数
 {
   delete [] a;
 }
 
void clear()//清零函数
 {
   memset(a,
0,sizeof(long)*max);
   length 
= 1;
 }
 friend ostream 
&operator << (ostream & output,BigInt &t)
 {
   output 
<< t.a[t.length-1];
   
for(int i=t.length-2;i>=0;i--)
             output 
<< setw(4<< setiosflags(ios::right) << setfill('0'<< t.a[i];
   
return output;
 }
 
void operator = (const BigInt & t)
 {
   
if(t.a==a)
    
return;
   delete a;
   a 
= new long [t.length];
   
for(int i=0;i<t.length;i++)
    a[i] 
= t.a[i];
   length 
= t.length;
 }
 
void operator = (const string & temp)
 {
   
int tLength = temp.length() - 1;
   length
=tLength/4+1;
   
for(int i=0;tLength>2;i++,tLength-=4)
    a[i]
=(temp[tLength]-'0')+(temp[tLength-1]-'0')*10+(temp[tLength-2]-'0')*100+(temp[tLength-3]-'0')*1000;
   
if(tLength==2)
    a[length
-1]=(temp[tLength]-'0')+(temp[tLength-1]-'0')*10+(temp[tLength-2]-'0')*100;
   
else if(tLength==1)
    a[length
-1]=(temp[tLength]-'0')+(temp[tLength-1]-'0')*10;
   
else if(!tLength)
    a[length
-1]=(temp[tLength]-'0');
 }
 BigInt 
operator + (const BigInt & t)
 {
   BigInt sum;
   
for(int i=0;i<t.length||i<length;i++)
   {
    sum.a[i]
+=a[i]+t.a[i];
    
if(sum.a[i]>9999)
    {
     
int j=i;
     
while(sum.a[j]>9999)
     {
      sum.a[j
+1]+=sum.a[j]/10000;
      sum.a[j]
%=10000;
      j
++;
     }
    }
   }
   
for(int i=sum.max-1;i>=sum.length;i--)
             
if(sum.a[i])
    {
                 sum.length
=i+1;
                 
break;
    }
    
return sum;
 }
 BigInt 
operator * (const BigInt & t)
 {
   BigInt product;
   
for(int i=0;i<t.length;i++)
    
for(int j=0;j<length;j++)
    {
     product.a[i
+j]+=a[j]*t.a[i];
     
if(product.a[i+j]>9999)
     {
      
int q=i+j;
      
while(product.a[q]>9999)
      {
       product.a[q
+1]+=product.a[q]/10000;
       product.a[q]
%=10000;
       q
++;
      }
     }
    }
    
for(int i=product.max-1;i>=product.length;i--)
     
if(product.a[i])
     {
      product.length
=i+1;
      
break;
     }
     
return product;
 }
};
BigInt s[MaxLength
+1];
int main()
{
 
int n;
 s[
0]= "1";
 s[
1]="1";s[2]="2";s[3]="5";
 
for(int i=4;i<101;i++)
   
for(int j=0;j<i;j++)
//    s[i] = add(s[i],mult(s[j],s[i-j]));
    s[i] = s[i]+s[j]*s[i-j-1];
 
while(cin>>&& n != -1)
 {
   cout
<<s[n]<<endl;
 }
 
return 0;
}

 

 

posted on 2010-06-02 20:07 superKiki 阅读(895) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

常用链接

留言簿

随笔档案

文章分类

我的好友

一些常去的网站

搜索

最新评论

阅读排行榜

评论排行榜