FZU - 1064 教授的测试【卡特兰数】

一年一度的研究生面试又快要来临了。为了测试学生对树结构的认识,同时也检验他们的编程能力,福州大学计算机系把面试的一项内容定为:要求学生们编程按编号顺序打印出节点个数不少于m的所有二叉树。
二叉树编号规则如下:
仅有一个节点的树编号为1。
当满足以下条件之一时,定义二叉树a的编号比b大:
1. a的节点数比b多。
2. 若a的节点数与b相等,且a的左子树编号比b的左子树大。
3. a的节点数和左子树编号都和b相等,且a的右子树编号比b的右子树大。
二叉树的节点用大写X表示,例如:

这里写图片描述
当然当m较大时,检验答案对错的工作也是很繁重的,所以教授只打算对其中的若干个编号的二叉树进行抽查,他想麻烦你编制一个程序能够产生编号为n的二叉树的标准答案。
Input
输入数据由多组数据组成。每组数据仅一个整数,表示n (1≤n≤10^8)的值。输入数据以n=0表示结束,该数据不要处理。
Output
对于每组数据,输出仅一行,即你求出的标准答案。
二叉树的输出格式为:
(左子树){若左子树为空则省略}X{根}(右子树){若右子树为空则省略}
其中{…}中的内容是说明,不必输出。例如,在上图中编号为5的树可表示为X((X)X);编号为6的树表示为(X)X(X)。
Sample Input
20
0
Sample Output
((X)X(X))X

分析:
通过数据量可以看出,这个题肯定是先寻找规律,然后递归输出。
然而,问题就在于,这规律怎么找,还有这该死的括号。
可也正是这个该死的括号,渐渐引导人们想起卡特兰数,自然我是好久想不出来的。

卡特兰数公式:
h(n)=h(n1)(4n2)/(n+1);

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e3 + 10;
#define ll long long int
ll f[maxn];
int n;
bool flag;
void get_f(int x, int y)
{
    if (x == 0)
        return;
    for (int i = 0; i < x; i++)
    {
        if (y <= f[i] * f[x - i - 1])
        {
            if (flag)
            {
                printf("(");
                get_f(i, (y - 1) / f[x - i - 1] + 1);
                printf("X");
                get_f(x - i - 1, (y - 1) % f[x - i - 1] + 1);
                printf(")");
            }
            else
            {
                flag = 1;
                get_f(i, (y - 1) / f[x - i - 1] + 1);
                printf("X");
                get_f(x - i - 1, (y - 1) % f[x - i - 1] + 1);
            }
            break;
        }
        else
        {
            y -= f[i] * f[x - i - 1];
        }
    }
}
int main()
{
    f[0] = f[1] = 1;
    for (int i = 2; i < maxn; i++)//ka te lan
    {
        f[i] = 1ll * f[i - 1] * (4 * i - 2) / (i + 1);
    }
    while (~scanf("%d", &n)&&n)
    {
        flag = 0;
        for (int i = 1; i < maxn; i++)
        {
            if (n <= f[i])
            {
                get_f(i, n);
                break;
            }
            else
            {
                n -= f[i];
            }
        }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值