【算法笔记】递归:椰子 二叉树

码农题:椰子

题目描述

hlgz的校长听说孩子们学习压力都很大,于是校长集体组织去夏威夷旅游(明显不现实←_←),有些不懂事的家伙喜欢爬到椰子树上玩,每次一个孩子从树上把一个椰子扔到地上来,(如下图所示),每一个椰子重量都不相同,最初这些椰子都挂在一棵很高的树上,它们被固定在一根与地面平行的轴线上。

当游戏开始时,一个椰子沿着与地面垂直的方向落到地面上指定的位置,如果椰子X落到了地上(因为地平线高度为0,故椰子在地上的高度为1),它会待在那里不动,如果它落在了另一个椰子上,就会出现下面几种情况: (1)如果椰子Y的左右两边都已有椰子(下图中用“O”表示),则椰子会停在那里不动。

X
     X
OYO => OYO

(2)如果椰子Y的两边都没有其它的椰子,并且椰子X比椰子Y重,那么椰子X将取代椰子Y的位置,同时椰子Y会滑落到其右侧的位置;但是如果椰子X比椰子Y轻,那么椰子X将直接滑落到Y的左侧。(下图中用“-”表示空位)

X
-Y- => -XY (X比Y重)

X
-Y- => XY- (X比Y轻)

(3)如果椰子Y只有一边有一个其它的椰子,发生的情况如下图所示:

X
-YO => YXO (X比Y重)

X
-YO => XYO (X比Y轻)

X
OY- => OXY (X比Y重)

X
OY- => OYX (X比Y轻)

注意:每当椰子X或Y滑落到一个不同的位置时,它会继续滑落直到待到一个不能再滑落的位置为止。 你的任务是输出每一个椰子最终的位置。
输入格式

输入文件的第一行有一个正整数T(T<=30),表示接下来测试数据的个数。 每一个测试数据由一个整数N(1<=N<=1000)开始,表示要掉落的椰子数量,接下来有N行,每行有两个整数Pi和Wi(1<=Pi,Wi<=1000),Pi表示第i个椰子在水平方向的坐标,Wi表示其重量,任意两个椰子的重量都不相同。
输出格式

对于每一个测试数据,输出N行,每一行有一个坐标(Xi,Yi),表示第i个椰子最终的位置,Xi表示高度,Yi表示其在水平方向上的坐标。请在每两组测试数据结果之间输出一个空行。
样例数据

input

2
2
1 1
1 2
4
1 1
2 2
1 3
1 4

output

1 2
1 1

1 0
1 2
1 1
2 1

注释

纯模拟
数据规模与约定

保证T≤30、1≤N≤1000、1≤Pi,Wi≤1000T≤30、1≤N≤1000、1≤Pi,Wi≤1000

时间限制:1s1s

空间限制:256MB

完全可以看出,这是一道毒瘤的码农题,我们应该进行分类讨论下落的情况并进行代替与椰子左移和右移的处理,即:
1.椰子下落时,如果下面没有,直接落下.
2.椰子下落是,下面有椰子,且左右都有,直接落下
3.椰子下落时,若左右没有,则继续模拟判断
4.椰子下落是,若左右只有一个,则判断是左边有还是右边有,再进一步判断中量进行处理.
同时,如果椰子进行左移或者右移时,下面是空的话,相当于水平坐标不同的位置重新掉落了一遍,所以需要进一步的递归,直到递归结束为止.
有一个坑点:
也许初始的椰子所在的横坐标都是符合范围的,但是椰子可能会在靠近横坐标远点的地方不断往左掉,自然就会产生越界错误.所以我们需要加上一定的偏移量.
可以看出,算法难度不大,代码实现难度很大.奉上百行代码:

#include<bits/stdc++.h>
using namespace std;
#define Coconumber 1000
struct GO_P
{
    int x;//行坐标
    int y;//纵坐标
    int w;//物体的重量 
}c[100000]={};
int n;
int h[10000]={};//记录高度 
int top[10000]={};//记录顶端椰子编号 
void Memset()
{
    memset(c,0,sizeof(c));
    memset(h,0,sizeof(h));
    memset(top,0,sizeof(top));
}
void put(int num,int P)//num表示椰子的编号,P表示横坐标的位置 
{
    if (h[P]==0)//如果原来的地方没有放上去,那就放上去
    {
        h[P]++;
        top[P]=num; 
        c[num].x=P; 
        c[num].y=1;
        return;
    } 
    int downn=top[P];//下面的椰子编号 
    int downw=c[downn].w;//下面的椰子重量 
    int upn=num;//上面的椰子编号 
    int upw=c[num].w;//下面的椰子重量
    int nowh=h[P];//记录当前高度 
    int lefth=h[P-1];//记录左边高度
    int righth=h[P+1];//记录右边高度
    if (lefth>=nowh&&righth>=nowh)//如果左右两边都有椰子 
    {
        h[P]++;top[P]=num;
        c[num].x=P;c[num].y=h[P];//直接落在上面  
        return;
    } 
    if (lefth<nowh&&righth<nowh)//如果下面的椰子两边没有椰子 
    {
        if (upw>downw)//上面的重量大于下面的重量  
        {
            top[P]=num;
            c[num].x=P;c[num].y=h[P];//取代下面的椰子 
            put(downn,P+1);//下面的滑向右侧
            return; 
        }
        if (upw<downw)//如果上面的椰子体重小 
        {
            put(upn,P-1);//上面的椰子向左滑一格 
            return;
        }
    } 
    if (lefth<nowh&&righth>=nowh)//如果左边的没有右边的有
    {
        if (upw>downw)//如果上面的体重大
        {
            top[P]=num;
            c[num].x=P;c[num].y=h[P];
            put(downn,P-1); 
            return;
        } 
        if (upw<downw)
        {
            put(upn,P-1);
            return;
        }
    } 
    if (lefth>=nowh&&righth<nowh)
    {
        if (upw>downw)
        {
            top[P]=num;
            c[num].x=P;c[num].y=h[P];
            put(downn,P+1); 
            return;
        }
        if (upw<downw)
        {
            put(upn,P+1);
            return;
        }
    }
    return;
}
void write()
{
    for (int i=1;i<=n;i++)
        cout<<c[i].y<<' '<<c[i].x-Coconumber<<endl;
    cout<<endl;
    return;
}
int main()
{
    int T;
    cin>>T;
    while (T--)
    {
        Memset();
        cin>>n;
        for (int i=1;i<=n;i++)
        {
            int Pos,W;
            cin>>Pos>>c[i].w;
            Pos+=Coconumber;//为防止越界,横坐标右移1000位 
            put(i,Pos);
        }
        write();
    }
    return 0;
}

二叉树的遍历:美国血统

题目描述
农夫约翰非常认真地对待他的奶牛们的血统。然而他不是一个真正优秀的记帐员。他把他的奶牛们的家谱作成二叉树,并且把二叉树以更线性的“树的中序遍历”和“树的前序遍历”的符号加以记录而不是用图形的方法。 你的任务是在被给予奶牛家谱的“树中序遍历”和“树前序遍历”的符号后,创建奶牛家谱的“树的后序遍历”的符号。每一头奶牛的姓名被译为一个唯一的字母。(你可能已经知道你可以在知道树的两种遍历以后可以经常地重建这棵树。)显然,这里的树不会有多余26个的顶点。这是在样例输入和样例输出中的树的图形表达方式:
        C
       / \
       /  \
       B   G
      / \  /
     A D H
       / \
       E F
树的中序遍历是打印左子树,根和右子树;树的前序遍历是打印根,左子树和右子树;树的后序遍历是打印左子树,右子树和根。
输入格式
第一行: 树的中序遍历;第二行: 同样的树的前序遍历
输出格式
单独的一行表示该树的后序遍历。
样例数据
input
ABEDFCHG
CBADEFGH
output
AEFDBHGC

这道题的代码实现难度不大,主要是要理解如果根据数的前序和中序遍历求出树的后序遍历.
我们根据树的便利顺序,可以分为三种:
树的前序遍历:遍历顺序为:根,左,右
树的中序遍历,遍历顺序为:左,根,右
树的后序遍历,便利顺序为:左,右,根
那么,我们可以来推一下样例:
树的前序遍历是先根再左右,故前序遍历的结果的第一个为C根
得到C为根,再在中序遍历中找到A,根据其遍历顺序,A的左边为左子树,即ABEDF,那么右子树就是FG了.因此,我们只需要根据上一部的递归结果反复递归即可.

#include<bits/stdc++.h>
using namespace std;
string S1,S2;
void dfs_trees(int L1,int R1,int L2,int R2)//左子树的区间,右子树的区间
{
    if (R1<L1||R2<L2) 
        return;//如果左端点大于右端点显然不存在,退出
    for (int i=L1;i<=R1;i++)//枚举树的中序遍历的每一个点去找前序遍历的根节点来进行配对 
        if (S1[i]==S2[L2])
        {
            dfs_trees(L1,i-1,L2+1,L2-(L1-i));
            dfs_trees(i+1,R2,L2-(L1-i)+1,R2);
            cout<<S1[i];
        } 
}
int main()
{
    cin>>S1>>S2;
    dfs_trees(0,S1.length()-1,0,S2.length()-1);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值