poj1095
题意:给出n,要求输出第n个二叉树,二叉树编号规则如下图所示:
分析:g[i]表示有i个节点的二叉树,有多少种。f[i][j]表示有i个节点,且左子树有j个节点的树有多少种。
sumg[i]表示g数组前i个的和。sumf[i][j]表示f[i]数组前j项的和。
有g[i]=sum(f[i][j]),f[i][j]=g[j]*g[i - 1 - j]。
对于一个输入的n,我们先通过对sumg进行二分查找以确定节点数量,然后递归求解。
递归包含两个参数:1、当前子树节点数量tot。2、要求第num个具有这些节点的子树。
接下来在sumf[tot]中对num进行二分查找,即可确定其左子树节点个数x,从而确定其右子树的节点个数y。
通过g[y](右子树的变化数)可以判定左子树处于第几个形态。计算公式为(num - sumf[tot][x - 1]) / g[y];
同样可以确定右子树处于哪一种形态。计算公式为(num - sumf[tot][x - 1]) % g[y];
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> using namespace std; const long long maxx = 30; long long f[maxx][maxx], g[maxx], sumg[maxx], sumf[maxx][maxx], n; long long binarysearch(long long *array, long long start, long long end, long long goal) { long long l, r, mid; l = start; r = end; while (l < r) { mid = (l + r) / 2; if (array[mid] < goal) l = mid + 1; else r = mid; } return l; } void dfs(long long tot, long long num) { long long x = binarysearch(sumf[tot], 0, tot - 1, num), a, b; if (x > 0) { a = (num - sumf[tot][x - 1]) / g[tot - x - 1]; b = (num - sumf[tot][x - 1]) % g[tot - x - 1]; if (b == 0) b = g[tot - x - 1]; else a++; printf("("); dfs(x, a); printf(")"); num = b; } printf("X"); if (tot - 1 - x > 0) { printf("("); dfs(tot - x - 1, num); printf(")"); } } void work() { long long num; num = binarysearch(sumg, 0, maxx, n); n -= sumg[num - 1]; dfs(num, n); printf("\n"); } int main() { long long i, j; //freopen("t.txt", "r", stdin); f[1][0] = 1; g[0] = 1; sumg[0] = 0; g[1] = sumg[1] = 1; for (i = 2; i < maxx; i++) { g[i] = sumf[i][0] = f[i][0] = g[i - 1]; for (j = 1; j < i; j++) { f[i][j] = g[j] * g[i - 1 - j]; g[i] += f[i][j]; sumf[i][j] = sumf[i][j - 1] + f[i][j]; } sumg[i] = g[i] + sumg[i - 1]; } while (scanf("%d", &n) != EOF && n != 0) work(); return 0; }