catalan数

注意关于2n变为n+m的问题因为c(n+m,n)=c(n+m,m)所以答案会有多种形式

http://www.cnblogs.com/orchid/p/3307862.html

http://baike.baidu.com/link?url=MyYkW6jkcGAByis8isQOPZWqQseYXDCttriDn556S3hIK-rE3yIKqOp0K6w4oJMtjnHqsdhSZ-PT8se0_wTDNBpDKGZmfUc7rRp36Yq5-NB1SQV3IYWlKqp9CV75D6sfqL3ax9acMYNxC0nUgcKIadWl69oUp8n4yD3DJzcsLi5wi_ap8XTjJG7GL6KdVpRV

Catalan数计算及应用

Catalan数列是非常奇妙的一列数字,因为很多问题的解就是一个Catalan数。知道了这一规律,很多看似复杂的问题便可迎刃而解。那么什么是Catalan数,什么样的问题的解是Catalan数呢?

1,Catalan数

先来看一段Catalan数列:1,1,2,5,14,42,132,429,1430,4862,16796,即 h(0)=1,h(1)=1,h(2)=2,h(3)=5...

怎么求出来的呢?两种方式

(1) h(n)=h(0)*h(n-1)+h(1)*h(n-2)+...+h(n-1)*h(0). 这是一个递归的公式。

(2) h(n)=c(n2,n)/(n+1). 由这个公式可以直接求出h(n),不需要知道h(n-1)...h(0).

在直接想要知道h(n)的时候,公式(2)很好用,但是在解决一些算法问题的时候,第一个公式更有用些,因为这一类算法全都能搞成公式(1)的样子求解。

2,Catalan数的应用

(1) n对括号有多少种匹配方式?

比如一对括号肯定有一种匹配方式(),两对括号呢,两种()()和(()),n对呢?

可以用分治的思想来。假设第一个(可以和i个)匹配了,即(...)...这个样子,那么整个大问题可以分成两个子问题,这种情况下有多少个配对等于红色点点部分的子问题有的配对数目乘以绿色点点部分的子问题配对数目。所有的匹配数目就等于所有有第一个括号配对的选择而带来的子问题的解的和。即n对括号匹配数目f(n)为,

f(n)=f(0)*f(n-1)+f(1)*f(n-1)+...+f(n-1)*f(0)

怎么理解?

f(0)*f(n-1)就是:()...第一个(与第一个)配对了,产生的两个子问题就是左边的0对括号的匹配和右面n-1对的括号的匹配。

f(1)*f(n-1)就是:(())..第一个(与第二个)配对了,产生的子问题就是左边的1对括号的匹配和右面n-2对括号的匹配。

而这个f(n)的公式是不是就是Catalan的递归公式呢?完全相同,因此n对括号的匹配数就是Catalan数h(n).

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

也是分治的思想,先通过一个括号将大问题分成小问题。比如(a1×a2)×(a3×a4×a5×a6...),然后再解决两个小问题的括号化问题。所以n个矩阵相乘的括号方案f(n)可以表达为

f(n)=f(1)*f(n-1)+f(2)*f(n-2)+...+f(n-1)*f(1).

所以f(n)=h(n-1)

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

进栈相当于左括号,出栈相当于右括号,这个问题和问题1是等价的。比如,123的进栈出栈序列可以表示为112332,红色表示进栈,绿色表示出栈,红色的对应(,绿色对应)。那么这个序列其实就是()(()).

所以这个问题的解还是h(n).

(4)n个节点构成的二叉树,共有多少种情形?

 有n个节点,这些节点的值不重要,重要的是如果选择第i个节点作为树的根,那么这棵树的左子树就会有i-1个节点,右子树就会有n-i-1个节点。

所以总共可能的二叉树有

f(n)=f(0)*f(n-1)+f(1)*f(n-2)....+f(n-1)*f(0)

也是Catalan数h(n)

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

以其中一个点为基点,编号为0,然后按顺时针方向将其他点依次编号。那么与编号为0相连点的编号一定是奇数,否则,这两个编号间含有奇数个点,势必会有个点被孤立,即在一条线段的两侧分别有一个孤立点,从而导致两线段相交。设选中的基点为A,与它连接的点为B,那么A和B将所有点分成两个部分,一部分位于A、B的左边,另一部分位于A、B的右边。然后分别对这两部分求解即可。所以,

f(n) = f(0)*f(n-2) + f(2)*f(n-4) + f(4)*f(n-6) + ......f(n-4)*f(2) + f(n-2)*f(0)。

(0)*f(n-2)表示编号0的点与编号1的点相连,此时位于它们右边的点的个数为0,而位于它们左边的点为2n-2。依次类推。

f(0) = 1, f(2) = 1, f(4) = 2。结合递归式,不难发现f(2n) 等于h(n)。

(6)求一个凸多边形区域划分成三角形区域的方法数?

设凸边行为ABCDEF...,以凸多边形的一边为基,设这条边的2个顶点为A和B。从剩余顶点中选1个记为X,可以将凸多边形分成三个部分,中间是一个三角形(ABX),左右两边分别是两个凸多边形(B(BX之间的点)X,AX(从X到A的点)),然后求解左右两个凸多边形。

设问题的解f(n),其中n表示顶点数,那么f(n) = f(2)*f(n-1) + f(3)*f(n-2) + ......f(n-2)*f(3) + f(n-1)*f(2)。f(2)*f(n-1)表示三个相邻的顶点构成一个三角形,那么另外两个部分的顶点数分别为2和n-1。

      设f(2) = 1,那么f(3) = 1, f(4) = 2, f(5) = 5。结合递推式,不难发现f(n) 等于h(n-2)

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

5元的人来是一个进栈,10元的人来是一个出栈。如果这个问题只考虑进栈和出栈的方式,不考虑具体的人,那么应该有h(n)中安排方式。也可以就考虑这些5元钱,它们总是要都进来一遍,然后又会全出的。

(8)标准二维表问题。设n是一个正整数,2×n的标准二维表是有正整数1,2,...,2n组成的2×n数组,该数组的每行从左到右递增,每列从上到下递增。2×n的标准二维表全体记为Tab(n)。例如当n=3时,Tab(3)二维表如下图所示:

1 2 3  1 2 4  1 2 5  1 3 4  1 3 5

4 5 6    3 5 6  3 4 6  2 5 6  2 4 6

这个问题可以转为为问题(1)。对每个配对的括号从左到右编号,左括号对应的编号放在第一行,右括号对应的编号放在第二行,因为编号从左到右按照顺序,而且右括号的编号肯定比左括号的编号大。比如:()()()对应[1 3 5][2 4 6]这个表,()(())对应[1 3 4][2 5 6]这个表。

因此Tab(n)=h(n).

(9)n个节点的二叉树的不同形态的个数

 不同形态的二叉树的数目恰好是前序序列均为1,2,3...n的二叉树所能的到的中序序列的数目。而中序遍历的过程实质上是一个点进栈和出栈的过程。二叉树的形态确定了其节点进栈和出栈的顺序,也确定了其中序序列。因此这个问题就变成问题3.

 ###交换后不符合要求的多了一个,n个1,n个0(交换后n+1个0,n-1个0),总数目=c(2n,n)-c(2n,n+1)=c(2n,n)/(n+1)=h(n)。 c(2n,n)为从2n中选n个1,c(2n,n+1)为从2n个中选n+1个0(第二项也就是不符合的一般是让正确的多一个),

原理

编辑
令h(0)=1,h(1)=1,catalan数满足递推式 [1]   :
h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)*h(0) (n>=2)
例如:h(2)=h(0)*h(1)+h(1)*h(0)=1*1+1*1=2
h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=1*2+1*1+2*1=5
另类递推式 [2]   :
h(n)=h(n-1)*(4*n-2)/(n+1);
递推关系的解为:
h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
递推关系的另类解为:
h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)

Buy the Ticket

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3253    Accepted Submission(s): 1356


Problem Description
The "Harry Potter and the Goblet of Fire" will be on show in the next few days. As a crazy fan of Harry Potter, you will go to the cinema and have the first sight, won’t you?

Suppose the cinema only has one ticket-office and the price for per-ticket is 50 dollars. The queue for buying the tickets is consisted of m + n persons (m persons each only has the 50-dollar bill and n persons each only has the 100-dollar bill).

Now the problem for you is to calculate the number of different ways of the queue that the buying process won't be stopped from the first person till the last person. 
Note: initially the ticket-office has no money. 

The buying process will be stopped on the occasion that the ticket-office has no 50-dollar bill but the first person of the queue only has the 100-dollar bill.
 


 

Input
The input file contains several test cases. Each test case is made up of two integer numbers: m and n. It is terminated by m = n = 0. Otherwise, m, n <=100.
 


 

Output
For each test case, first print the test number (counting from 1) in one line, then output the number of different ways in another line.
 


 

Sample Input
    
    
3 0 3 1 3 3 0 0
 


 

Sample Output
    
    
Test #1: 6 Test #2: 18 Test #3: 180
Recommend
Eddy
 
 
 
解题思路(转): ( C(m+n, n) - C(m+n, m+1) ) * m! * n! 化简即 (m+n)! * (m-n+1) /  (m + 1 )

推導過程如下:
m個人拿50,n個人拿100
1、如果n > m,那麼排序方法數為0,這一點很容易想清楚
2、現在我們假設拿50的人用‘0’表示,拿100的人用‘1’表示。
如果有這麼一個序列0101101001001111。
當第K個位置出現1的個數多餘0的個數時就是一個不合法的序列了
假設m=4,n=3的一個序列是:0110100 。顯然,它不合法,現在我們把它稍微變化一下:
把第二個1(這個1前面的都是合法的)後面的所有位0變成1,1變成0.
就得到0111011這個序列1的數量多餘0的數量,顯然不合法,但現在的關鍵不是看這個序列是不是合法的
關鍵是:他和我們的不合法序列0110100成一一對應的關係。
也就是說任意一個不合法序列(m個0,n個1),都可以由另外一個序列(n-1個0和m+1個1)/(所以c(n+m,n-1)==c(n+m,m+1))得到。
另外我們知道,一个序列要麼是合法的,要麼是不合法的
所以,合法序列數量 = 序列總數量 - 不合法序列的總量
序列總數可以這樣計算 m+n個位置中,選擇n個位置出來填上1,所以是C(m+n,n).
不合法序列的數量就是: m+n個位置中,選擇m+1個位置出來填上1,所以是C(m+n,m+1).
然後每個人都是不一樣的,所以需要全排列m! * n!.
所以最後的公式為:( C(m+n,n) - C(m+n,m+1) ) * m! * n!
化簡即為:(m+n)!*(m-n+1)/(m+1)

推廣:
如果原來有p張50元的話,那麼不合法的序列的數量應該是:任意一個不合法序列(m個0,n個1),都可以由另外一個序列(n-1個0和m+1+p個1)得到,所以是m+n個位置中,選擇m+1+p個位置,出來填上1所以是C(m+n,m+1+p),接下來簡化就不推了

 

[cpp]  view plain  copy
  1. #include<stdio.h>  
  2. #include<string.h>  
  3. int a[201][501],b[501];  
  4. int main()  
  5. {  
  6.  memset(a,0,sizeof(a));  
  7.  a[0][0]=1;a[1][0]=1;  
  8.  int temp,k=0;   
  9.  int i,j,s,sort,m,n,u;  
  10.  for(i=2;i<201;i++)               //预处理阶乘  
  11.  {  
  12.   for(j=0,s=0;j<501;j++)  
  13.   {  
  14.    temp=i*a[i-1][j]+s;  
  15.    a[i][j]=temp%10;  
  16.    s=temp/10;  
  17.      
  18.   }  
  19.  }  
  20.  sort=0;  
  21.  while(scanf("%d%d",&m,&n)!=EOF&&(m||n))  
  22.  {  
  23.   printf("Test #%d:\n",++sort);  
  24.   if(m<n) {printf("0\n");continue;}  
  25.   i=m-n+1;  
  26.   memset(b,0,sizeof(b));  
  27.   for(j=0,s=0;j<501;j++)         
  28.   {  
  29.    temp=i*a[m+n][j]+s;  
  30.    b[j]=temp%10;  
  31.    s=temp/10;  
  32.   }  
  33.   k=500;  
  34.   while(k>=0&&b[k]==0) k--;  
  35.   for(j=k,s=0,u=0;j>=0;j--)  
  36.   {  
  37.    temp=s*10+b[j];  
  38.    if(u==0)  
  39.    {   
  40.     if(temp/(m+1)>0)                 //对于第1次输出要谨慎,防止先输出0  
  41.     {u=1;printf("%d",temp/(m+1));}  
  42.    }  
  43.    else printf("%d",temp/(m+1));                      
  44.    s=temp%(m+1);  
  45.   }  
  46.   printf("\n");  
  47.  }  
  48.  return 0;  
  49.   
  50. }  
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值