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
1 2 3 10
1 2 5 16796HintThe result will be very large, so you may not process it by 32-bit integers.
【分析】:又是一道没有思路的题目。。。好吧,又听说了一个神秘的数组——卡特兰数
1.卡塔兰数是组合数学中一个常在各种计数问题中出现的数列。以比利时的数学家欧仁·查理·卡塔兰
卡塔兰数的一般项公式为
前几项为 (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的另一个表达形式为 所以,Cn是一个自然数;这一点
在先前的通项公式中并不显而易见。这个表达形式也是André对前一公式证明的基础。
它也满足
- //这个在本题中用来推导卡特兰数!
- 很重要的一个推导式!!
这提供了一个更快速的方法来计算卡塔兰数。
卡塔兰数的渐近增长为
它的含义是左式除以右式的商趋向于1当n → ∞。(这可以用n!的斯特灵公式来证明。)
所有的奇卡塔兰数Cn都满足。所有其他的卡塔兰数都是偶数。
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; }
我没有搞懂啊啊啊。。。只能先知道这种算法了。。。。。。。。。。。。。。