1.我们可以用一个简单的dp算出用i个节点组成二叉树的方案数,令f[i]表示用i个节点组成二叉树的方案数,则f[i]=Σ(f[j]*f[i-1-j]), 因为我们考虑选1个点为根,枚举左子树的节点数,然后根据乘法原理计算。
2.我们可以设计一个递归函数打印答案,不难想到具体的框架是【打印左子树-->打印X-->打印右子树】,然后这样递归做下去,令get_tree(m,s)表示打印用m个节点,组成序号为s的二叉树,这里的序号的意思是用正好m个节点构成的二叉树的排名。
3.我们怎么在递归函数中得知其左右子树需要的节点数和排名呢?我们可以先算出用m个节点,其中i个节点用于左子树,构成排名小于s的i的最大值, 令那个小于s的个数称为now,此时的i+1的值称为pos,那么s-now即为多出的排名数,pos为此时左节点数(因为当左节点数为i时排名小于s,因此左节点数必为i+1,即pos), 右子树排名最大值是f[m-1-pos],因此我们需要用多出的排名先尽量填右子树,右子树填满后再将其清空增加左子树,即L=(s-now-1)/f[m-1-pos]+1,R=(s-now-1)%f[m-1-pos]+1。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[50],n;
void get_tree(int m,int s)
{
int now=0,pos;
if (m==1)
{
printf("X");
return;
}
for (int i=0;i<m;i++)
{
now+=(f[i]*f[m-1-i]);
pos=i;
if (now>=s) break;
}
now-=(f[pos]*f[m-1-pos]);
int L=(s-now-1)/f[m-1-pos]+1;
int R=(s-now-1)%f[m-1-pos]+1;
if (pos!=0)
{
printf("(");
get_tree(pos,L);
printf(")");
}
printf("X");
if (m-1-pos!=0)
{
printf("(");
get_tree(m-1-pos,R);
printf(")");
}
return;
}
int main()
{
f[0]=f[1]=1;
for (int i=2;i<=20;i++)
for (int j=0;j<i;j++) f[i]+=f[j]*f[i-1-j];
while (scanf("%d",&n)==1)
{
if (n==0) break;
int sum=0,ord;
for (int i=1;sum<n;i++)
{
sum+=f[i];
ord=i;
}
sum=n;
for (int i=1;i<ord;i++) sum-=f[i];
get_tree(ord,sum);
printf("\n");
}
return 0;
}