HDU.1023 Train Problem II【大数除法、卡特兰数】--用于求出栈的n种方式(3.15)

Train Problem II

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


Problem Description
As we all know the Train Problem I, the boss of the Ignatius Train Station want to know if all the trains come in strict-increasing order, how many orders that all the trains can get out of the railway.
 

Input
The input contains several test cases. Each test cases consists of a number N(1<=N<=100). The input is terminated by the end of file.
 

Output
For each test case, you should output how many ways that all the trains can get out of the railway.
 

Sample Input
  
  
1 2 3 10
 

Sample Output
  
  
1 2 5 16796
Hint

The result will be very large, so you may not process it by 32-bit integers.

【分析】:又是一道没有思路的题目。。。好吧,又听说了一个神秘的数组——卡特兰数

1.卡塔兰数组合数学中一个常在各种计数问题中出现的数列。以比利时的数学家欧仁·查理·卡塔兰 

(18141894)命名。

卡塔兰数的一般项公式为 C_n = \frac{1}{n+1}{2n \choose n} = \frac{(2n)!}{(n+1)!n!}

前几项为 (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, ...(增长速度飞快!但是这个题要算到100啊!)

性质

Cn的另一个表达形式为C_n = {2n\choose n} - {2n\choose n-1} \quad\mbox{ for }n\ge 1 所以,Cn是一个自然数;这一点

在先前的通项公式中并不显而易见。这个表达形式也是André对前一公式证明的基础。

递推关系

C_0 = 1 \quad \mbox{and} \quad C_{n+1}=\sum_{i=0}^{n}C_i\,C_{n-i}\quad\mbox{for }n\ge 0.

它也满足

C_0 = 1 \quad \mbox{and} \quad C_{n+1}=\frac{2(2n+1)}{n+2}C_n,//这个在本题中用来推导卡特兰数!
很重要的一个推导式!!

   这提供了一个更快速的方法来计算卡塔兰数。

   卡塔兰数的渐近增长为

C_n \sim \frac{4^n}{n^{3/2}\sqrt{\pi}}

它的含义是左式除以右式的商趋向于1当n → ∞。(这可以用n!的斯特灵公式来证明。)

所有的奇卡塔兰数Cn都满足n=2^k-1。所有其他的卡塔兰数都是偶数。

2.卡特兰数的应用:

出栈次序(也就是这个题)

  一个(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?
  常规分析
  首先,我们设f(n)=序列个数为n的出栈序列种数。同时,我们假定第一个出栈的序数是k。
  第一个出栈的序数k将1~n的序列分成两个序列,其中一个是1~k-1,序列个数为k-1,另外一
个是k+1~n,序列个数是n-k。
  此时,我们若把k视为确定一个序数,那么根据乘法原理,f(n)的问题就等价于——序列个
数为k-1的出栈序列种数乘以 序列个数为n - k的出栈序列种数,即选择k这个序数的f(n)=f(k-1)
×f(n-k)。而k可以选1到n,所以再根据加法原理,将k 取不同值的序列种数相加,得到的总序列种数为:f(n)=f(0)f(n-1)
+f(1)f(n-2)+……+f(n-1)f(0)。
  看到此处,再看看卡特兰数的递推式,答案不言而喻,即为f(n)=h(n)
= C(2n,n)/(n+1)=  c(2n,n)-c(2n,n+1)(n=1,2,3,……)。
  最后,令f(0)=1,f(1)=1。

【代码如下】:

//本程序涉及【大数除法】【卡特兰数】等知识点 #include <iostream> #include <stdio.h> #include <algorithm> #include <math.h> #include <string.h> #include <memory.h> #define MAX 101 #define BASE 10000// using namespace std; void multiply(int a[], int len, int b) {     for(int i=len-1, carry=0; i>0; i--)//从最后一位开始相乘     {         carry += b*a[i];         a[i] = carry % BASE;         carry /= BASE;     } } void divide(int a[], int len, int b)//很巧妙的除法 {     for(int i=0, div=0; i<len; ++i)//从最高位除起     {         div = div * BASE + a[i];         a[i] = div / b;         div %= b;     } } int main() {     int h[101][MAX], i, j;     memset(h[1], 0, MAX*sizeof(int));//“第一行”每一位都置零     for(i=2,h[1][MAX-1]=1; i<=100; ++i)     {//递归得前100项的卡特兰数!         memcpy(h[i], h[i-1], MAX*sizeof(int));//按字节拷贝,保证了两个指向数组的一致性         multiply(h[i], MAX, 4*i-2);//h[i]*=(4*i-2)         divide(h[i], MAX, i+1);//h[i]/=i+1     }     while(cin>>i && i>=1 && i<=100)     {         for(j=0; j<MAX && h[i][j]==0; ++j);//从0位开始搜索,找到不为0的第一个数         printf("%d", h[i][j++]);//巧妙的输出,第一位可能不足4位,就地输出!         for( ; j<MAX; j++)         {             printf("%04d", h[i][j]);//中间为有0,左边加0,凑足4位         }         printf("\n");     }     //system(pause);     return 0; }

我没有搞懂啊啊啊。。。只能先知道这种算法了。。。。。。。。。。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值