HDOJ-1100 Trees made to order

一、题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=1100
二、题目分析

对二叉树的所有形态顺序编号,编号规则是:节点数越多的编号越大;节点数相等,左子树节点数越多的越大;节点数相等,左子树节点数也相等,则依此规则比较右子树。

现给定一个正整数,依题目要求输出对应编号的二叉树形态。

三、求解思路

由题目输出格式要求,很直观地联想到使用深度优先搜索dfs,以当前二叉树对应的编号为条件,依次递归输出左子树和右子树。

 1 int main(void)
 2 {
 3     int n;
 4     unsigned int arr[20] = {0};
 5
 6     init(arr, 20);
 7
 8     while (scanf("%d", &n) != EOF && n != 0) {
 9         dfs(n, arr, 20);
10         printf("\n");
11     }
12
13     return 0;
14 }

其中arr存储的是不同节点数对应的二叉树形态数,即arr[i]表示有i个节点的二叉树总共有多少种形态。

 1 void dfs(int n, unsigned int *arr, int num)
 2 {
 3     int left, right;
 4
 5     if (n == 1) {
 6         printf("X");
 7         return ;
 8     }
 9
10     getChildren(&left, &right, n, arr, num);
11
12     if (left != 0) {
13         printf("(");
14         dfs(left, arr, num);
15         printf(")");
16     }
17     printf("X");
18     if (right != 0) {
19         printf("(");
20         dfs(right, arr, num);
21         printf(")");
22     }
23 }

通过getChildren得到左右子树对应的编号,然后递归搜索即可。

 1 void getChildren(int *left, int *right, int n, unsigned int *arr, int num)
 2 {
 3     int i;
 4     int nall, nleft, nright;
 5     int div, rem;
 6
 7     for (i = 1; i < num; i++) {
 8         if ((unsigned)n > arr[i]) {
 9             n -= arr[i];
10         } else {
11             nall = i;
12             break;
13         }
14     }
15
16     for (i = 0; i < nall; i++) {
17         if ((unsigned)n > arr[i] * arr[nall - i - 1]) {
18             n -= arr[i] * arr[nall - i - 1];
19         } else {
20             nleft = i;
21             break;
22         }
23     }
24     nright = nall - nleft - 1;
25
26     div = n / arr[nright];
27     rem = n % arr[nright];
28
29     *left = *right = 0;
30
31     if (nleft != 0) {
32         for (i = 1; i < nleft; i++) {
33             *left += arr[i];
34         }
35         *left += rem == 0 ? div : div + 1;
36
37     if (nright != 0) {
38         for (i = 1; i < nright; i++) {
39             *right += arr[i];
40         }
41         *right += rem == 0 ? arr[nright] : rem;
42     }
43 }

计算左右子树对应序号的思路:

  1. 计算总节点数nall、左子树节点数nleft和右子树节点数nright;
  2. 在计算总节点数和左子树节点数的同时,刨除所有总节点数小于nall的二叉树形态,刨除所有左子树节点数小于nleft的二叉树形态;
  3. 那么下面如何确定当前左子树是nleft个节点的二叉树中的第几个?当前右子树又是nright个节点的二叉树中的第几个?且看一例
  4. 这里假定左子树有三种形态,分别是1,2,3,右子树有两种形态,分别是a,b。额,什么,没有哪个节点总数对应三种二叉树形态?拜托,举例子撒,画五种形态的很累的。什么,画的太难看?88
  5. 由这个小例子可以看出,要想确定左右子树对应的序号,需要依据右子树的形态数 ,对应的就是代码中的div,rem以及下面计算的blablabla

嗯,差不多就这样。为什么是arr[20]?怎么计算i个节点对应的二叉树形态数?wiki一下Catalan numbers, ok?

附完整代码:

  1 /* http://acm.hdu.edu.cn/showproblem.php?pid=1100 */
  2
  3 #include <stdio.h>
  4
  5 void    init(unsigned int *arr, int N);
  6 void    dfs(int n, unsigned int *arr, int num);
  7 void    getChildren(int *left, int *right, int n, unsigned int *arr, int num);
  8
  9 int main(void)
 10 {
 11     int n;
 12     unsigned int arr[20] = {0};
 13
 14     init(arr, 20);
 15
 16     while (scanf("%d", &n) != EOF && n != 0) {
 17         dfs(n, arr, 20);
 18         printf("\n");
 19     }
 20
 21     return 0;
 22 }
 23
 24 void init(unsigned int *arr, int N)
 25 {
 26     int i, j;
 27
 28     arr[0] = arr[1] = 1;
 29     for (i = 2; i < N; i++) {
 30         for (j = 0; j < i; j++) {
 31             arr[i] += arr[j] * arr[i - j - 1];
 32         }
 33     }
 34 }
 35
 36 void dfs(int n, unsigned int *arr, int num)
 37 {
 38     int left, right;
 39
 40     if (n == 1) {
 41         printf("X");
 42         return ;
 43     }
 44
 45     getChildren(&left, &right, n, arr, num);
 46
 47     if (left != 0) {
 48         printf("(");
 49         dfs(left, arr, num);
 50         printf(")");
 51     }
 52     printf("X");
 53     if (right != 0) {
 54         printf("(");
 55         dfs(right, arr, num);
 56         printf(")");
 57     }
 58 }
 59
 60 void getChildren(int *left, int *right, int n, unsigned int *arr, int num)
 61 {
 62     int i;
 63     int nall, nleft, nright;
 64     int div, rem;
 65
 66     for (i = 1; i < num; i++) {
 67         if ((unsigned)n > arr[i]) {
 68             n -= arr[i];
 69         } else {
 70             nall = i;
 71             break;
 72         }
 73     }
 74
 75     for (i = 0; i < nall; i++) {
 76         if ((unsigned)n > arr[i] * arr[nall - i - 1]) {
 77             n -= arr[i] * arr[nall - i - 1];
 78         } else {
 79             nleft = i;
 80             break;
 81         }
 82     }
 83     nright = nall - nleft - 1;
 84
 85     div = n / arr[nright];
 86     rem = n % arr[nright];
 87
 88     *left = *right = 0;
 89
 90     if (nleft != 0) {
 91         for (i = 1; i < nleft; i++) {
 92             *left += arr[i];
 93         }
 94         *left += rem == 0 ? div : div + 1;
 95
 96     if (nright != 0) {
 97         for (i = 1; i < nright; i++) {
 98             *right += arr[i];
 99         }
100         *right += rem == 0 ? arr[nright] : rem;
101     }
102 }

好吧,使用dfs(int n, int nall, int *arr, int num)或许能写的更简单些。

时间: 2024-10-11 16:36:17

HDOJ-1100 Trees made to order的相关文章

POJ 1095 Trees Made to Order(计数问题)

Trees Made to Order Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 6882   Accepted: 3940 Description We can number binary trees using the following scheme: The empty tree is numbered 0. The single-node tree is numbered 1. All binary tre

POJ 1095 Trees Made to Order

题目大意: 按照题意将数用二叉树的形式表示出来.将二叉树按照要求形式输出. 解题思路: 由于有图示,可以轻易看出这个顺序跟卡特兰数列有关.且对于任意一个树的任意子树来说,右子树相当于分针,左子树相当于秒针.也就是说当右子树要变换到下一种状态时,左子树要将它所能变换的状态全变换完才可以. 下面是代码: #include <set> #include <map> #include <queue> #include <math.h> #include <ve

POJ 1095 Trees Made to Order 最详细的解题思路

题目来源:Trees Made to Order 题目大意:根据下面的规则给一棵二叉树编号: 规则1:如果二叉树为空,则编号为0: 规则2:如果二叉树只有一个节点,则编号为1: 规则3:所有含有m个节点的二叉树的编号小于所有含有m+1个节点的二叉树的编号: 规则4:如果一棵含有m个节点的二叉树(左子树为L,右子树为R)的编号为n,要想其它含有m个节点的二叉树的编号如果大于n,则需要满足两个条件中的任意一个:1.左子树的编号大于L的编号:2.左子树的编号等于L的编号,但是右子树的编号大于R的编号.

hdoj 2841Visible Trees(容斥原理)

题意:m*n的格点上有m*n棵树,从(0,0)点可以看到多少棵树 假设x与1到m有num[x]个数互质,即1到m中与x有非1的公约数的个数 为m-num[x], 最后结果就是n*m-(num[1]+...num[n]) #include<stdio.h> #include<vector> #include<string.h> #include<iostream> using namespace std; const int N=100000+10; vect

[Swift]LeetCode894. 所有可能的满二叉树 | All Possible Full Binary Trees

A full binary tree is a binary tree where each node has exactly 0 or 2 children. Return a list of all possible full binary trees with N nodes.  Each element of the answer is the root node of one possible tree. Each node of each tree in the answer mus

杭电ACM分类

杭电ACM分类: 1001 整数求和 水题1002 C语言实验题——两个数比较 水题1003 1.2.3.4.5... 简单题1004 渊子赛马 排序+贪心的方法归并1005 Hero In Maze 广度搜索1006 Redraiment猜想 数论:容斥定理1007 童年生活二三事 递推题1008 University 简单hash1009 目标柏林 简单模拟题1010 Rails 模拟题(堆栈)1011 Box of Bricks 简单题1012 IMMEDIATE DECODABILITY

POJ百道水题列表

以下是poj百道水题,新手可以考虑从这里刷起 搜索1002 Fire Net1004 Anagrams by Stack1005 Jugs1008 Gnome Tetravex1091 Knight Moves1101 Gamblers1204 Additive equations 1221 Risk1230 Legendary Pokemon1249 Pushing Boxes 1364 Machine Schedule1368 BOAT1406 Jungle Roads1411 Annive

【转】对于杭电OJ题目的分类

[好像博客园不能直接转载,所以我复制过来了..] 1001 整数求和 水题1002 C语言实验题——两个数比较 水题1003 1.2.3.4.5... 简单题1004 渊子赛马 排序+贪心的方法归并1005 Hero In Maze 广度搜索1006 Redraiment猜想 数论:容斥定理1007 童年生活二三事 递推题1008 University 简单hash1009 目标柏林 简单模拟题1010 Rails 模拟题(堆栈)1011 Box of Bricks 简单题1012 IMMEDI

转载:hdu 题目分类 (侵删)

转载:from http://blog.csdn.net/qq_28236309/article/details/47818349 基础题:1000.1001.1004.1005.1008.1012.1013.1014.1017.1019.1021.1028.1029. 1032.1037.1040.1048.1056.1058.1061.1070.1076.1089.1090.1091.1092.1093. 1094.1095.1096.1097.1098.1106.1108.1157.116