题目大意
可以使用以下方案给二叉树编号:
空树编号为0。
单结点数编号为1。
所有m个结点的二叉树的编号小于(m+1)个结点的二叉树的编号。
若任何m个结点的二叉树,当左右子树数为L和R个时的二叉树编号为n,则下列情况下所有m个结点的二叉树,其编号都大于n:
左子树的编号大于L或者左子树编号为L,且右子树编号大于R。
序列中前10棵二叉树和编号为20的树如下
输入
给定多个样例,每一个样例包含一个整数n,1≤n≤500 000 000。n的值为0表示输入结束(不需要输出一棵空树)
输出
对每个样例输出一行,是该实例标号对应的树。用一下方案输出树:
没有子树的树,输出为X。
有左右子树L和R,输出为(L’)X(R’),L’ 和 R’ 表示 L 和 R。
若左子树为空,只需要输出 X(R’)。
若右子树为空,只需要输出 (L’)X。
样例
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)
解题思路
katalan数
分析图例,可知,当一个树包含了m + 1个结点,那么这棵树可能的结构就有:
(左子树结点数,右子树结点数)
(0,m),
(1,m-1),
(2,m-2),
(3,m-3),
… ,
(m-1,1),
(m,0)
那么包含m+1个结点的树种类便是以上所有每种树结构包含种类的和。
这种求法符合katalan数,即
h
(
n
)
=
h
(
0
)
∗
h
(
n
−
1
)
+
h
(
1
)
∗
h
(
n
−
2
)
+
.
.
.
+
h
(
n
−
1
)
∗
h
(
0
)
(
其
中
n
≥
2
)
h(n) = h(0)*h(n-1) + h(1)*h(n-2) + ...+ h(n-1)*h(0) (其中n≥2)
h(n)=h(0)∗h(n−1)+h(1)∗h(n−2)+...+h(n−1)∗h(0)(其中n≥2)
该递推关系的解为:
h
(
n
)
=
c
(
2
n
,
n
)
/
(
n
+
1
)
h(n)=c(2n,n)/(n+1)
h(n)=c(2n,n)/(n+1)
katalan数C++两种求法及katalan数的前20项:
//递归求法
void setCatalan() {
catalan[0] = 1;
catalan[1] = 1;
setC();
for (int i = 2; i < 20; i++) {
//递归求法
catalan[i] = 0;
for (int j = 0; j < i; j++) {
catalan[i] += catalan[j] * catalan[i - 1 - j];
}
}
}
//公式求法
void setC() {//构造组合数
for (int i = 0; i < 50; i++) {
C[0][i] = 0;
C[i][0] = 1;
}
for (int i = 1; i < 50; i++) {
for (int j = 1; j < 50; j++) {
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
}
}
void setCatalan() {
catalan[0] = 1;
catalan[1] = 1;
setC();
for (int i = 2; i < 20; i++) {
//公式法
catalan[i] = C[i*2][i] / (i + 1); }
}
/*
卡塔兰数前20项:
catalan[0] = 1
catalan[1] = 1
catalan[2] = 2
catalan[3] = 5
catalan[4] = 14
catalan[5] = 42
catalan[6] = 132
catalan[7] = 429
catalan[8] = 1430
catalan[9] = 4862
catalan[10] = 16796
catalan[11] = 58786
catalan[12] = 208012
catalan[13] = 742900
catalan[14] = 2674440
catalan[15] = 9694845
catalan[16] = 35357670
catalan[17] = 129644790
catalan[18] = 477638700
catalan[19] = 1767263190
*/
左右子树编号
通过katalan数,我们可以求出编号为n的树所包含的结点数,同时,我们也可以通过katalan数求出其左右子树的编号。
一棵树编号为n,我们可求出其结点数为m,
根据本题中树的排序为左0右m-1,左1右m-2,左 2右m-3的排列方式,且可知左0右m-1结构的种类数量为katalan[0]*katalan[m-1]
即可求出左右子树各自的数量。
设本棵树为包含m个结点的所有树里的第 A 棵树,左子树结点数为 L,右子树结点数为R。
那么,左子树的编号为:
包
含
L
个
结
点
的
第
一
棵
树
编
号
+
A
/
k
a
t
a
l
a
n
[
右
子
树
结
点
数
]
包含L个结点的第一棵树编号 + A/katalan[右子树结点数]
包含L个结点的第一棵树编号+A/katalan[右子树结点数]
右子树编号为:
包
含
R
个
结
点
的
第
一
棵
树
编
号
+
A
m
o
d
(
k
a
t
a
l
a
n
[
右
子
树
结
点
数
]
)
包含R个结点的第一棵树编号+A mod(katalan[右子树结点数])
包含R个结点的第一棵树编号+Amod(katalan[右子树结点数])
递归求解
当我们可以求出左右子树编号时,就可通过递归,求出这棵树的结构,并根据题目要求将结果输出即可。
AC代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
unsigned long long C[50][50];
unsigned long long catalan[20];//卡塔兰数
unsigned long long catalanSum[20];
void setC() {//构造组合数
for (int i = 0; i < 50; i++) {
C[0][i] = 0;
C[i][0] = 1;
}
for (int i = 1; i < 50; i++) {
for (int j = 1; j < 50; j++) {
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
}
}
void setCatalan() {
catalan[0] = 1;
catalanSum[0] = 0;
catalan[1] = 1;
catalanSum[1] = 1;
setC();
for (int i = 2; i < 20; i++) {
//递归求法
/*catalan[i] = 0;
for (int j = 0; j < i; j++) {
catalan[i] += catalan[j] * catalan[i - 1 - j];
}*/
//公式法
catalan[i] = C[i*2][i] / (i + 1);
catalanSum[i] = catalanSum[i - 1] + catalan[i - 1];
}
}
void ZOJ1062(int n) { // m:左右子树结点和
if (n == 1) {
printf("X");
return;
}
// 求编号为n的树的结点数
int sum = 0;
int m = 0;
while (true) {
if (sum + catalan[m] > n)
break;
sum += catalan[m];
m++;
}
//求左右子树各自的结点数。
int leftM = 0;//左子树的结点数
int rightM = 0;//右子树的结点数
int leftN = 0;//左子树的编号
int rightN = 0;//右子树的编号
while (m > 0) {
if (sum + catalan[leftM] * catalan[m - leftM - 1] > n)
break;
sum += catalan[leftM] * catalan[m - leftM - 1];
leftM++;
}
rightM = m - 1 - leftM;//根节点需要排除
//计算左右子树的编号
int a = n - sum;
leftN = catalanSum[leftM] + a / catalan[rightM];
rightN = catalanSum[rightM] + a % catalan[rightM];
//递归求出左右子树的排序
if (leftM > 0) {
printf("(");
ZOJ1062(leftN);
printf(")");
}
printf("X");
if (rightM > 0) {
printf("(");
ZOJ1062(rightN);
printf(")");
}
}
int main() {
setCatalan();
// for (int i = 0; i < 20; i++) {
// cout << "catalan[" << i << "] = " << catalan[i] << endl;
// }
int n;
while (scanf("%d", &n) && n) {
//for(int i = 0 ; i < 24 ; i++){
ZOJ1062(n);
printf("\n");
}
return 0;
}