题目链接>>FZU 1064 教授的测试
解法:卡特兰数的应用。用递归直接输出。根据这个树排名,来确定左子树的节点个数和排名,以及右子树的节点个数和排名。
关于卡特兰数的应用总结,可以参考这几篇文章
http://blog.csdn.net/dacc123/article/details/50922138
http://blog.csdn.net/myue5/article/details/7984061
https://wenku.baidu.com/view/c2f253d226fff705cc170ad6.html
看完上述文章后是不是理解为啥要用
Catalan
数来做了吗?
因为节数为
i
的二叉树最多有
定义
ctl
数组为
Catalan
数前17项
(为什么是17项,因为
∑i=116Catalani=48760366<108
∑i=117Catalani=178405156>108
)
定义sum数组为ctl数组的前缀和( sum 里面ctl[0]不算进去,因为没有排名为 0 的树)
如果
那么我们可以判断排名为 n 的二叉树有
这棵树在有 i+1 个节点的树中排名为 n−sumi
然后我们可以来递归搜索了,因为对于一棵树只要知道他子树的节点数和其排名就可以求出这棵树了
#include<cstdio>
int sum[18]={1,1,3,8,22,64,196,625,2055,6917,23713,82499,290511,1033411,3707851,13402696,48760366,178405156};
int ctl[18]={1,1,2,5,14,42,132,429,1430,4862,16796,58786,208012,742900,2674440,9694845,35357670,129644790};
void dfs(int x,int rank){
if(x==1){putchar('X');return;}//边界条件
int i=x,tot=0;//i表示右子树有几个点
while(tot<rank){i--;tot+=ctl[i]*ctl[x-i-1];}//左子树有x-i-1个节点时,右子树有i个节点时的总方案数
rank=rank-(tot-ctl[i]*ctl[x-i-1])-1;//减去前面那一堆算出当前树在 左子树有x-i-1个节点时,右子树有i个节点 的树中的排名
if(x-i-1>0)putchar('('),dfs(x-i-1,rank/ctl[i]+1),putchar(')');//在左子树中,排名每增加1 右子树要增加ctl[i]次
putchar('X');//令左子树在左子树的rank中为ranky,右子树为rankx,所以就有(ranky-1)*ctl[i]+rankx=rank(ranky∈[1,ctl[x-i-1]],rankx∈[1,ctl[x]])
if(i>0)putchar('('),dfs(i,rank%ctl[i]+1),putchar(')');//所以rankx=rank%ctl[i] ranky=rank/ctl[i]+1 但是由于rankx可能等于ctl[i]导致结果出错
}//所以不妨做个小处理(ranky-1)*ctl[i]+(rankx-1)=rank-1 这样就可以避免上述情况了,此时 rankx=rank%ctl[i]+1 ranky=rank/ctl[i]+1
int main(){
int i,n;
while(scanf("%d",&n),n!=0){
for(i=0;i<=17;i++)if(sum[i]<n&&n<=sum[i+1])break;
dfs(i+1,n-sum[i]);puts("");
}
return 0;
}
其实 Catalan 数列很神奇,但是在组合数学中还有许许多多的神奇数列,有兴趣的可以去了解一下,这里就不做赘述了。
谢谢观看!