【POJ 1095】Trees Made to Order(卡特兰+递归)

Time Limit: 1000MSMemory Limit: 10000K
Total Submissions: 7155Accepted: 4094

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)

Hint


Source






二叉树的种数——卡特兰数。

这部分不是重点,重点是输出树的方法。原本脑子里硬想怎么也想不出,在纸上画来画去,今天拿出来一张白纸,平静心态,然后就像原本就在纸上一样,思路跟着笔就那么渐渐出来了。

首先是画了前几项,因为没有专业工具,所以凑合看看……

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

节点数为4的我没有画全。
具体的 对于当前节点,只要想办法找出左右子树的形态,然后递归画出来就行了。

图中每个编号的树表示方式是当前节点和左右子树形态编号。在图中指定编号往下画一定可以画出所求的树。
看图中Id较大的,比较明显可以看出来规律,当前树节点为x的话,当前节点的树中,第一棵左右子树节点数为0 x-1
然后就是以节点数为x-1的树形态来推,假设x-1的树形态个数为Ct[x-1],那么每加一次Ct[x-1],左子树形态就上升一次,直到左子树到达最终态。
左子树节点数 ++     右子树节点数 ,然后接着分级增加,直到加到所求的Id,然后递归左右即可。

那么先考虑递归框架

void prt(LL x,bool f = 1)
{
    if(!x) return;

    if(f) putchar('(');

    prt();

    putchar('X');

    prt();

    if(f) putchar(')');
}

第一层传入参数 f = 0,不输出左右括号,其余节点,只要存在,那就要输出有右括号分界。

递归的结束x == 0 —->节点数为0

其余的,想办法找出左右形态 L R,然后画完左边,画当前’X’,再画右边就好了。

因为已知二叉树种数是卡特兰数,预处理一下即可。

填充完就出来最终代码了:

#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <ctime>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)

using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;

LL ct[233];
LL cts[233];
int tp;

void init()
{
    tp = 0;
    ct[tp] = 1;
    cts[tp++] = 0;
    while(cts[tp-1] < 5000000000)
    {
        cts[tp] = cts[tp-1]+ct[tp-1];
        ct[tp] = ct[tp-1]*(4*tp-2)/(tp+1);
        tp++;
    }
    //for(int i = 0; i < tp; ++i)
    //  printf("%lld %lld\n",ct[i],cts[i]);
}

void prt(LL x,bool f = 1)
{
    if(!x) return;

    LL l,r;

    int pos;

    //找出当前子树节点个数。
    for(int i = 0; ; ++i)
    {
        if(x < cts[i])
        {
            pos = i-1;
            break;
        }
    }

    //初始为 L:0 R:NodeNum-1
    l = 0;
    r = pos-1;
    x -= cts[pos];

    LL p1;
    p1 = 0;
    while(x-ct[r] >= 0)
    {
        x -= ct[r];
        p1++;
        if(p1 == ct[l]) 
        {
            p1 = 0;
            l++,r--;
        }
    }

    //printf("%lld-%lld\n",l,r);
    //printf("%lld %lld\n",cts[l]+p1,cts[r]+x);

    if(f) putchar('(');

    prt(cts[l]+p1);

    putchar('X');

    prt(cts[r]+x);

    if(f) putchar(')');
}

int main()
{
    //fread("");
    //fwrite("");

    init();

    LL x;

    while(~scanf("%lld",&x) && x)
    {
        prt(x,0);
        puts("");
    }

    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其中一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵中最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (递归dp) $O(nm)$ 对于这道题,我们可以使用递归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$中的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与递归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程中的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程中需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值