正文

Zju 1062 Trees Made to Order2006-08-26 15:17:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/cruxd/17906.html

分享到:

2043646 2006-08-26 14:29:40 Accepted 1062 C++ 00:00.00 432K St.Crux

给出一个N,求中序树形。

Sample Input

1
20
31117532
0

Sample Output

X
((X)X(X))X
(X(X(((X(X))X(X))X(X))))X(((X((X)X((X)X)))X)X)

这个题的思想是不断逼近。要构造一颗树,(最好)要知道他的左子树序列号和右子树序列号。这个一下子求不出来,但是我们可以先看一下能求出的东西。

N个节点数这儿有几棵树。An= C(2n, n) / (n + 1),这个可以求,打一个表就可。且可求得N<20。

N这整棵树是几个节点。这个就好求了。用An一个一个的依次减,马上就求出来。减出来的数还得存着,待会儿还得减。

左子树几个节点。这个有点技巧了。这要明白一规律,当左子树有l个节点,右子树r个节点时,这样的树有几棵。答案是Al * Ar。这是全题的关键。而且这树是按左子树的节点树依次排下来的。这就好办了,和上面一样,一个一个挨个减下来。算出来以后右子树也出来了。这就是左子树的节点数和右子树的结点数。

最后就是这个序列号了。就拿这4,5,6,7,8来说吧。减到现在,就成了0,1,0,0,1。这有什么规律呢?没错,0,1分别是2号和3号在他们那一组(2棵树的)里的序列号。现在要用数学公式来说的话,就是:

设Sl为左边的序列号(在他们那一组里的,以下同)Sr为右边的,Al是左边节点数树的总个数,Ar同,t是N减到现在还剩的那个数。则可得:Sl = t / Ar, Sr = t  % Ar。因为每棵树是从右开始排,填满了Ar * Al种排法就结束,换到下一种Ar -1的排法里去了,也不归这个排法的管了。左边呢?就Al种排法,一个一个来,比作齿轮的话,就先得让右边的Ar轮一遍,才轮到他动一格。于是这个题就变成简单的排列组合了。

具体的算法边界处还得考虑,这个也是挺麻烦的一个地方。

#include <cstdio>

int a[20] = { 1, 1, 2, 5, 14, 42, 132,
  429, 1430, 4862, 16796, 58786, 208012,
  742900, 2674440, 9694845, 35357670,
  129644790, 477638700 }, b[20], n;

void dfs(int x)
{
 int s, l, r, i, t = x, xl, xr;
 for(i = 0; i < 20; i ++)
 {
  if(t < a[i])
   break;
  t -= a[i];
 }
 s = i - 1;
 for(i = 0; i <= s; i ++)
 {
  if(t < a[i] * a[s - i])
   break;
  t -= a[i] * a[s - i];
 }
 l = i, r = s - i;
 xl = t / a[r] + b[l], xr = t % a[r] + b[r];
 if(xl)
 {
  printf("(");
  dfs(xl);
  printf(")");
 }
 printf("X");
 if(xr)
 {
  printf("(");
  dfs(xr);
  printf(")");
 }
}

 
void init()
{
 int i, t = 0;
 b[0] = 0;
 for(i = 0; i < 19; i ++)
 {
  t += a[i];
  b[i + 1] = t;
 }
}

int main()
{
 //freopen("in.txt", "r", stdin);
 init();
 while(scanf("%d", &n) && n)
 {
  dfs(n);
  printf("\n");
 }
 return 0;
}

 

 

阅读(5044) | 评论(2)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

loading...
您需要登录后才能评论,请 登录 或者 注册