Combinatorics——HDUOJ 1100 - Trees Made to Order(二叉树 + 卡特兰数)

原题

  • Problem 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 trees having m nodes have numbers less than all those having m+1 nodes.
    Any binary tree having m nodes with left and right subtrees L and R is numbered n such that all trees having m nodes numbered > n have either
    Left subtrees numbered higher than L, or
    A left subtree = L and a right subtree numbered higher than R.
    The first 10 binary trees and tree number 20 in this sequence are shown below:
    这里写图片描述
    Your job for this problem is to output a binary tree when given its order number.

  • Input

    Input consists of multiple problem instances. Each instance consists of a single integer n, where 1 <= n <= 500,000,000. A value of n = 0 terminates input. (Note that this means you will never have to output the empty tree.)

  • Output

    For each problem instance, you should output one line containing the tree corresponding to the order number for that instance. To print out the tree, use the following scheme:
    A tree with no children should be output as X.
    A tree with left and right subtrees L and R should be output as (L’)X(R’), where L’ and R’ are the representations of L and R.
    If L is empty, just output X(R’).
    If R is empty, just output (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)

要搞懂这道题,首先需要了解二叉树和卡特兰数Catalan
这里写图片描述
这里写图片描述
卡特兰数传送门:
http://www.cnblogs.com/jackge/archive/2013/05/19/3086519.html(帖子)
https://baike.baidu.com/item/%E5%8D%A1%E7%89%B9%E5%85%B0%E6%95%B0/6125746?fr=aladdin(百度百科)
代码参考:http://www.cnblogs.com/jackge/archive/2013/05/19/3086526.html
卡特兰数 类似题目:http://blog.csdn.net/hackbuteer1/article/details/7450250

解题思路:
根据下面的规则给一棵二叉树编号:

  • 如果二叉树为空,则编号为0;
  • 如果二叉树只有一个节点,则编号为1;
  • 所有含有m个节点的二叉树的编号小于所有含有m+1个节点的二叉树的编号;
  • 如果一棵含有m个节点的二叉树(左子树为L,右子树为R)的编号为n,要想其它含有m个节点的二叉树的编号如果大于n,则需要满足两个条件中的任意一个:
    • 1、左子树的编号大于L的编号;
    • 2、左子树的编号等于L的编号,但是右子树的编号大于R的编号。

代码:

#include <stdio.h>
long long C[21] = { 1 };//第21个卡特兰数达到65亿
void Catalan()//预处理卡特兰数
{
    int i = 1;
    for (; i <= 20; i++)
    {
        C[i] = C[i - 1] * (4 * i - 2) / (i + 1);
    }
}
void PrintfAnswer(int n, long long rank)//输出n个节点中,排序第rank的二叉树
{
    if (n == 1)//只有1个节点
    {
        printf("X");
        return;
    }
    if(rank <= C[n - 1])//排名靠前,即没有根节点没有左子树
    {
        printf("X");
        printf("(");
        PrintfAnswer(n - 1, rank);
        printf(")");
    }else if(rank >= C[n] - C[n - 1] + 1)//排名靠后,即没有根节点没有右子树
    {
        printf("(");
        PrintfAnswer(n - 1, rank - (C[n] - C[n - 1]));
        printf(")");
        printf("X");    
    }else
    {
        int t = n - 2;
        long long temp;
        long long ExceptLeftAllTree = C[n] - C[n - 1];//根节点除了只有左子树外,一共还有的树的种类(包括只有右子树)
        int LeftTreeNum;
        int RightTreeNum;
        long long NewRank;//找出的新的种类的树中的排序(求出左右子树数量的  该种类  中的排序)
        for (int i = t; i >= n - 1 - t; i--)//i为根节点左子树数量
        {
            temp = C[i] * C[n - 1 - i];//例如:若n = 5,则C[3]*C[1](即左子树为3,右子树为1的类型有temp种)
            if (rank > ExceptLeftAllTree - temp)//检测是否符合左子树为i,又右子树为n-1-i
            {
                LeftTreeNum = i;
                break;
            }else
            {
                ExceptLeftAllTree -= temp;
            }
        }
        //求出左右子树各多少之后,算出在该类型树中的排序NewRank 
        NewRank = rank - (ExceptLeftAllTree - temp);
        RightTreeNum = n - LeftTreeNum - 1;
        printf("(");
        long long LeftTreeRank;//左子树中的排序
        //这个地方需要注意:右子树的优先级是小于左子树的,所以每次判断子数中的排序时,注意左子树每变换一次,右子数变换C[RightTreeNum]次
        if(NewRank < C[RightTreeNum])//注意排序
        {
            LeftTreeRank = 1;
        }else if(NewRank % C[RightTreeNum] == 0)
        {
            LeftTreeRank = NewRank / C[RightTreeNum];
        }else
        {
            LeftTreeRank = NewRank / C[RightTreeNum] + 1;
        }
        PrintfAnswer(LeftTreeNum, LeftTreeRank);
        printf(")X(");
        long long RightTreeRank;//右子树中的排序
        if(NewRank % C[RightTreeNum] == 0)
        {
            RightTreeRank = C[RightTreeNum];
        }else
        {
            RightTreeRank = NewRank % C[RightTreeNum];
        }
        PrintfAnswer(RightTreeNum, RightTreeRank);
        printf(")");
    }
}
int main()
{
    int i;
    long long n;
    int c;
    Catalan();//预处理卡特兰数
    while (scanf("%lld",&n)!=EOF && n)
    {
        for (i = 1;;i++)
        {
            if (n > C[i])
            {
                n -= C[i];//求出在当前节点所有排序中的名次
            }else
            {
                c = i;//求出当前是i节点组成的二叉树
                break;
            }
        }
        PrintfAnswer(c, n);
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值