Combinatorics——HDUOJ 1100

来源:互联网 发布:fifaol3 超玩数据库 编辑:程序博客网 时间:2024/05/21 10:51

原题

  • 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;}
原创粉丝点击