组合数学,递推(Catalan number , 卡特兰数)
题目:给n个数字,能构建成多少种二叉排序树。这个问题并不难,用递归的思想就能解决。当空树即n=0时方案数为1,当n=1时方案数同样为1。当n>=2时,我们把n个数按升序排列,如果我们选ai作为数树根,那么a1……ai-1必定在有子树,ai+1……an必定在左子树,左子树有i-1个元素,同样是排序二叉树,相当于问这个i-1个元素又能组成多少个二叉排序树,右子树有n-i个元素,同时是二叉排序树,相当于问这n-i个元素能组成多少个二叉排序树,方案数为两者相乘。
对于每个ai作为树根的计算方法如上,而所有的ai都能作为树根,所以不能总结出公式
h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)h(0) (n>=2)
由于n去到1000,是个很大的数,要用到高进度
但是发现用上面的公式去推必须为二重循环,会超时,所以没办法去找报告,才知道这个数列其实是卡特兰数,然后查了卡特兰数的相关资料,得到了不同的递推公式,下面这个递推公式就是正解
h(n)=h(n-1)*(4*n-2)/(n+1);
用这个公式,高精度乘法为int乘高精度,高精度除法为int除高精度,还是比较好写的
#include <cstdio> #include <cstring> #define N 2000 int maxlen; struct num { int a[N+50],len; }big[N]; void add(struct num &p ,struct num t) { int max=p.len>t.len?p.len:t.len; int i,c=0; for(i=0; i<max; i++) { p.a[i]+=(t.a[i]+c); c=p.a[i]/10; p.a[i]%=10;} if(c) p.a[i++]=c; p.len=i; } void mul(struct num &p , struct num q , int x) { int s; for(int i=0; i<q.len; i++) { s=q.a[i]*x; struct num t; int j=i; memset(t.a,0,sizeof(t.a)); t.len=0; while(s) { t.a[j]=s%10; s/=10; j++; } t.len=j; add(p,t); } } void div(struct num &p , int y) { int i=p.len-1, x=0 , k=0 , j; int ans[N+50]; memset(ans,0,sizeof(ans)); while(i>=0) { while(x<y && i>=0) { x=(x*10+p.a[i]); i--; ans[k++]=0; } k--; ans[k++]=x/y; x%=y; } for(i=0; ;i++) if(ans[i]) break; //消除前导0 for(p.len=k-i , j=p.len-1; i<k ; j--,i++) p.a[j]=ans[i]; } int main() { memset(big,0,sizeof(big)); big[0].a[0]=1; big[0].len=1; big[1].a[0]=1; big[1].len=1; for(int n=2; n<=1000; n++) { //h(n)=h(n-1)*(4*n-2)/(n+1) mul(big[n] , big[n-1] , 4*n-2); div(big[n] , n+1); } int n; while(scanf("%d",&n)!=EOF) { for(int i=big[n].len-1; i>=0; i--) printf("%d",big[n].a[i]); printf("\n"); } return 0; }