http://acm.hdu.edu.cn/showproblem.php?pid=1133
查看维基百科,对卡特兰数公式证明是这样的:
http://zh.wikipedia.org/wiki/%E5%8D%A1%E5%A1%94%E5%85%B0%E6%95%B0
令1表示进栈,0表示出栈,则可转化为求一个2n位、含n个1、n个0的二进制数,满足从左往右扫描到任意一位时,经过的0数不多于1数。显然含n个1、n个0的2n位二进制数共有个,下面考虑不满足要求的数目。
考虑一个含n个1、n个0的2n位二进制数,扫描到第2m+1位上时有m+1个0和m个1(容易证明一定存在这样的情况),则后面的0-1排列中必有n-m个1和n-m-1个0。将2m+2及其以后的部分0变成1、1变成0,则对应一个n+1个0和n-1个1的二进制数。反之亦然(相似的思路证明两者一一对应)。
从而。证毕。
从以上证明启发,来得到HDU1133的公式:
懒得打字,在纸上列好草稿,发上来:
对最后结果化简后:
最后的公式为 : ( C(m+n, n) - C(m+n, m+1) ) * m! * n! 化简即 (m+n)! * (m-n+1) / (m+1)
最后给出代码,大数处理方案的:
用到大数乘法(乘小的数)和除法(除小的数):
#include <iostream> #include <string> using namespace std; #define MAX 100 #define BASE 10000 void multiply(int a[],int Max,int b) //大数乘小数 { int i,array=0; for (i=Max-1; i>=0; i--) { array+=b*a[i]; a[i] = array%BASE; array /= BASE; } } void divide(int a[], int Max, int b) //大数除小数 { int i,div=0; for (i=0;i<Max; i++) { div = div*BASE + a[i]; a[i] = div / b; div %= b; } } int fact[205][MAX]; void setFact () //求出0-200的阶乘值 { fact[0][MAX-1] = fact[1][MAX-1] = 1; for ( int i = 2; i <= 200; ++ i ) { memcpy ( fact[i] , fact[i-1] , MAX * sizeof ( int ) ); multiply ( fact[i] , MAX , i ); } } void outPut ( int ctl[MAX] ) { int i = 0; while ( i < MAX && ctl[i] == 0 )//去掉前面的为0的项 { i ++ ; } printf ( "%d", ctl[i++] ); while ( i < MAX ) { printf ( "%04d", ctl[i++] ); } putchar ( '\n' ); } int res[MAX]; int main () { int M,N; int ca = 1; setFact(); //打表 while ( cin >> M >> N , M + N ) { printf ( "Test #%d:\n",ca++ ); if ( N > M ) { puts ( "0" ); continue; } memcpy ( res , fact[M+N] , MAX * sizeof ( int ) ); //阶乘 ( m + n )! multiply ( res, MAX, M - N + 1 ); // ( m + n )! * ( m-n+1 ) divide ( res, MAX, M + 1 ); // ( m + n )! * ( m-n+1 ) / ( m+ 1 ) outPut ( res ); } return 0; }