hdu 1100 Trees Made to Order

http://acm.hdu.edu.cn/showproblem.php?pid=1100

  今晚做了一下标记为组合数学的题。这题归类组合数学应该是因为这题用到二叉树计数的原理吧。这次是完全没有参考其他资料的了~

  稍微解释一下题意,这题就是将二叉树标号,然后给出标号,让你画出二叉树来。标号的规则比较长,总的来说就是结点多的标号大,左结点标号大的大,如果左结点标号相同,右结点标号大的大。

  刚开始的时候,我就光靠数字和图形间的关系来推测构图的方法,然后发现要解这题必须要找到根本,就是二叉树计数。然后我就打了个卡特兰数的表,用来关联题目的条件。这时可以找到一个方法来找出左右子树的模样。

  我描述一下方法:因为二叉树计数的递推是依靠左右子树的组合数来产生的,如果按照那种递推方法,将标号不停的减少,最后可以找到左右子树分别有多少个结点。然后,剩余量就是指在该组中是第几种组合方式。接着就是要计算左右子树分别属于它的数量中的第几种组合方式。刚刚说了一下题意,可以发现要右子树变到没得变,左子树才会改变一次,这就是我的代码中取模的操作,得到lrest和rrest。之后的每一个子结点都按照这种方法来找到左右子树的模样,因此我就进行递归的操作。从而得到树的模样!

  最后输出的时候就是一个中序遍历输出,整体基本完成了!

参考代码:

View Code
 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 typedef __int64 ll;
 5 const int maxn = 20;
 6 const ll maxsum = 500000000;
 7 ll tri[maxn << 1][maxn << 1];
 8 ll sum[maxn], rec[maxn];
 9 int cur, l[maxn], r[maxn];
10 
11 void build(){
12     memset(tri, 0, sizeof(tri));
13     tri[0][0] = 1;
14     for (int i = 1; i < maxn << 1; i++){
15         tri[i][0] = 1;
16         for (int j = 1; j <= i; j++){
17             tri[i][j] = tri[i - 1][j - 1] + tri[i - 1][j];
18         }
19     }
20     sum[0] = rec[0] = 1;
21     for (int i = 1; i < maxn; i++){
22         rec[i] = tri[i << 1][i] / (i + 1);
23         sum[i] = sum[i - 1] + rec[i];
24 #ifndef ONLINE_JUDGE
25         printf("%2d : %12I64d %12I64d\n", i, sum[i], rec[i]);
26 #endif
27     }
28 }
29 
30 void con(int rt, int rest, int cnt){
31     if (rest == 1 && cnt == 1){
32         return ;
33     }
34     cnt--;
35     for (int i = 0; i <= cnt; i++){
36         int tmp = rec[i] * rec[cnt - i];
37 
38 
39         if (rest > tmp) rest -= tmp;
40         else{
41             int lrest = (rest - 1) / rec[cnt - i] + 1;
42             int rrest = (rest - 1) % rec[cnt - i] + 1;
43 
44             if (i){
45                 l[rt] = ++cur;
46                 con(cur, lrest, i);
47             }
48             if (cnt - i){
49                 r[rt] = ++cur;
50                 con(cur, rrest, cnt - i);
51             }
52 
53             break;
54         }
55     }
56 }
57 
58 void print(int rt){
59     if (~l[rt]){
60         putchar('(');
61         print(l[rt]);
62         putchar(')');
63     }
64     putchar('X');
65     if (~r[rt]){
66         putchar('(');
67         print(r[rt]);
68         putchar(')');
69     }
70 }
71 
72 void deal(int n){
73     int cnt = 0;
74 
75     while (n >= sum[cnt]) cnt++;
76 #ifndef ONLINE_JUDGE
77     printf("%d\n", cnt);
78 #endif
79     for (int i = 0; i < cnt; i++)
80         l[i] = r[i] = -1;
81     cur = 0;
82     n -= sum[cnt - 1] - 1;
83     con(0, n, cnt);
84     print(0);
85     puts("");
86 }
87 
88 int main(){
89     int n;
90 
91     build();
92     while (~scanf("%d", &n) && n)
93         deal(n);
94 
95     return 0;
96 }

  还是要多做组合数学的题来加快推理的速度啊!

——written by Lyon

原文地址:https://www.cnblogs.com/LyonLys/p/hdu_1100_Lyon.html