Help Jimmy(有趣的题目)

Help Jimmy

这个题目好有趣,所以特地写一下,真有趣
Description
“Help Jimmy” 是在下图所示的场景上完成的游戏。
在这里插入图片描述
场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。
Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。
设计一个程序,计算Jimmy到底地面时可能的最早时间。
Input
第一行是测试数据的组数t(0 <= t <= 20)。每组测试数据的第一行是四个整数N,X,Y,MAX,用空格分隔。N是平台的数目(不包括地面),X和Y是Jimmy开始下落的位置的横竖坐标,MAX是一次下落的最大高度。接下来的N行每行描述一个平台,包括三个整数,X1[i],X2[i]和H[i]。H[i]表示平台的高度,X1[i]和X2[i]表示平台左右端点的横坐标。1 <= N <= 1000,-20000 <= X, X1[i], X2[i] <= 20000,0 < H[i] < Y <= 20000(i = 1…N)。所有坐标的单位都是米。
Jimmy的大小和平台的厚度均忽略不计。如果Jimmy恰好落在某个平台的边缘,被视为落在平台上。所有的平台均不重叠或相连。测试数据保证问题一定有解。
Output
对输入的每组测试数据,输出一个整数,Jimmy到底地面时可能的最早时间。
Sample Input
1
3 8 17 20
0 10 8
0 10 13
4 14 3
Sample Output
23

这条题目分析起来难度不大,用常规的递归思路去想也是可以的。最优子问题在这里也相当明显。当到一块板的时候,我们看看到底走左边快还是走右边快,分解子问题就搞定了。

不过在计算的时候注意一些细节就万事大吉了!

POJ Help Jimmy
Jimmy那么可怜,那就help help他吧
为了节省空间,详细的分析在代码当中体现

/*这里借用一些小伙伴的分析过程
分析:
将板子由高到低按从0到n编号,起始点的为0 
不妨认为Jimmy开始的位置是一个编号为0,长度为0的板子
设LeftMinTime(k)表示从k号板子左端到地面的最短时间
RightMinTime(k)表示从k号板子右端到地面的最短时间
if ( 板子k左端正下方没有别的板子) {
    if( 板子k的高度 h(k) 大于Max)
        LeftMinTime(k) = ∞;不符合要求就是无穷
    else
        LeftMinTime(k) = h(k);
}
else if( 板子k左端正下方的板子编号是m )
    LeftMinTime(k) = h(k)-h(m) +
        Min( LeftMinTime(m) + Lx(k)-Lx(m),
            RightMinTime(m) + Rx(m)-Lx(k));
} 
*/ 
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define MAX_N 1000
#define INFINITE 1000000
int t,n,x,y,maxHeight;
class Platform
{//定义平台类,当然c语言结构体也行
   public:
    int Lx, Rx, h;//Lx表示做边界横坐标,Rx表示右边界横坐标,h表示高度 
    bool operator < (const Platform & p2) const
     {//符号重载,sort的时候能按h从高到低排 
        return h > p2.h;
    }
};
Platform platForms[MAX_N + 10];//平台 
int leftMinTime[MAX_N + 10];//走左边的最小时间 
int rightMinTime[MAX_N + 10];//走右边的最小时间 
int L[MAX_N + 10];//
//l表示现在这块板的编号,越在上面的编号越小,bleft表示是否向左边走 
//因为这题分为向左和向右两种情况 
int MinTime( int l, bool bLeft )//l表示现在这块板的编号,越在上面的编号越小,bleft表示是否向左边走 
{
    //初始化x和y坐标,如果是去左边,就走到左边边上,如果去右边,就走到右边边上 
    int y = platForms[l].h;
    int x;
    //如果是去左边,就走到左边边上,如果去右边,就走到右边边上
    if(bLeft)
        x = platForms[l].Lx;
    else
        x = platForms[l].Rx;
    int i;
    for( i = l + 1;i <= n;i ++ ) {//找到现在这块板下面的那块板 
        if( platForms[i].Lx <= x && platForms[i].Rx >= x)//判断从当前条能跳到的小一块板子上 
        break;
    }
    if( i <= n ) {// 板子k左端正下方有别的板
        if( y - platForms[i].h > maxHeight )// 跳到这块平台的高度如果大于Max
        return INFINITE;//返回无限大 
    }
    else {// 板子k左端正下方没有别的板
        if( y > maxHeight )//板子k的高度 h(k) 大于Max
            return INFINITE;//返回无限大 
        else
            return y;//如果可以直接跳下,就输出y 
    }
    int nLeftTime = y - platForms[i].h + x - platForms[i].Lx;//现在平台与下一块平台的高度差以及下一块平台左边界的距离 
    int nRightTime = y - platForms[i].h + platForms[i].Rx - x;//现在平台与下一块平台的高度差以及下一块平台右边界的距离 
    if( leftMinTime[i] == -1 ) //等于-1表示我初始化过 ,如果还可以向左我们就向左 
        leftMinTime[i] = MinTime(i,true);//向左进入子问题 
    if( L[i] == -1 )//等于-1表示我初始化过 ,如果还可以向右我们就向右 
        L[i] = MinTime(i,false);//像右进入子问题 
    nLeftTime += leftMinTime[i];//左边固定花费的时间加上下一场左边这样的时间 
    nRightTime += L[i];//右边固定花费的时间加上下一场右边这样的时间 
    //返回左边和右边走中值小的那一个 
    if( nLeftTime < nRightTime )
        return nLeftTime;
    return nRightTime;
}

int main() {
   // freopen("in.txt","r",stdin);  //测试时候用一下,提交代码不用馆
    scanf("%d",&t);//读入t组数据 
    for( int i = 0;i < t; i ++ ) {//对每组数据进行操作 
        memset(leftMinTime,-1,sizeof(leftMinTime));//初始化leftMinTime 
        memset(L,-1,sizeof(rightMinTime));//初始化L 
        scanf("%d%d%d%d",&n, &x, &y, &maxHeight);//读入数据 
        platForms[0].Lx = x; platForms[0].Rx = x;
        platForms[0].h = y;//起始点初始化 
        for( int j = 1; j <= n; j ++ )//读入平台信息 
            scanf("%d%d%d",&platForms[j].Lx,& platForms[j].Rx, & platForms[j].h);
        sort(platForms,platForms+n+1);//对平台由高到低排序 
        printf("%d\n", MinTime(0,true));//MinTime()方法求下平台的最小时间 
    }
return 0;
}

近期写的这些算法体都属于基础算法的范围当中,并不包含太多数据结构的内容,那一部分会在之后更新的数据结构与算法当中详细的进行讨论。

学会程序和算法,走遍天下都不怕
疫情期间,今天不走天下,就走走我的房间书台吧
在这里插入图片描述Gary哥的书台

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值