POJ - 1661(Help Jimmy)

题目描述

在这里插入图片描述
在这里插入图片描述

解题思路

非常有意思的动态规划题。首先思考一个问题,如果最小下落路径已知,那么不管是从高处下落,还是从地面向上起跳,只要路径不变,那么路径长也是不会改变的。所以,理解了这点,就可以解决第一个问题,动态规划的起始点与最终状态。由于从起点下落,起点已知,但是下落的路径有多种可能,所以无法确定最终状态,因此我们可以将起点作为最终状态,由地面向起点动规。
第二个问题就是转移的过程:
在这里插入图片描述
如上图,如果从平台1的左端点下落,则只可能掉落到平台2上,而无法穿过平台2掉落到平台3上。
在这里插入图片描述如上图,如果从平台1左端点下落,则由于平台2的右端点在平台1的左侧,即无法从平台1下落到平台2上。
上面介绍了大致的问题,那么下面就可以确定状态表示和动态转移方程了
1.状态表示:
–>dp[i][0]表示从第i个平台的左端点下落,经过若干平台到达地面的最短时间
–>dp[i][1]表示从第i个平台的右端点下落,经过若干平台到达地面的最短时间
2.状态转移:
对于第i个平台,人可能从第i个平台的左右两端下落,且只有可能从第i-1~0个平台(0表示地面)的某个平台转移而来,假设符合条件的平台是平台k(还要保证不会摔死),则人下落到平台k后,也可能走向左端或者右端。所以有
    从第i个平台的左端下落,则必有value[i].l >= value[k].l && value[i].l <= value[k].r(l表示左端点坐标,r表示右端点坐标)
–>a.从平台k的左端转移而来:dp[k][0] + value[i].l - value[k].l
–>b.从平台k的右端转移而来:dp[k][1] + value[k].r - value[i].l
dp[i][0] = min{a, b} + value[i].h - valu[k].h(h表示平台离地面高度)
    从第i个平台的右端下落,则必有value[i].r >= value[k].l && value[i].r <= value[k].r
–>a.从平台k的左端转移而来:dp[k][0] + value[i].r - value[k].l
–>b.从平台k的右端转移而来:dp[k][1] + value[k].r - value[i].r
dp[i][1] = min{a, b} + value[i].h - valu[k].h(h表示平台离地面高度)
最终答案即从两种状态中选择最小的,如假设平台n+1是起点
res = min{dp[n+1][0], dp[n+1][1]}

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1005;
const int INF = 0x3f;
typedef struct Value{
    int l, r;   //l表示左端点,r表示平台右端点
    int h;      //h表示平台高度
}Value;
Value value[MAXN];

bool cmp(Value t1, Value t2) {      //按照平台高度从低到高排序
    return t1.h < t2.h;
}

int dp[MAXN][2];    //dp[i][0]表示从第i个平台左端落下时,到达地面最短时间,dp[i][1]表示从第i个平台右端落下时,到达地面最短时间
int main() {
int main() {
    int t;
    cin >> t;
    while(t--) {
        memset(dp, INF, sizeof(dp));
        int N, X, Y, MAXH;      //MAXH表示最大下落高度
        cin >> N >> X >> Y >> MAXH;
        for(int i = 1; i <= N; i++) {
            cin >> value[i].l >> value[i].r >> value[i].h;
        }
        sort(value+1, value+N+1, cmp);      //按照高度排序
        
        value[0].l = -20005, value[0].r = 20005;       //将地面当作0号平台,且长度无限长 
        value[0].h = 0; 
        value[N+1].l = value[N+1].r = X;            //将起点当作最后一个平台,其长度为0
        value[N+1].h = Y;
        
        dp[0][0] = dp[0][1] = 0;
        for(int i = 1; i <= N+1; i++) {
            //找寻从左端点落下,可以掉落到的平台位置
            int j = i - 1;
            while(j >= 0 && value[i].h - value[j].h <= MAXH) {
                if(value[i].l >= value[j].l && value[i].l <= value[j].r) {
                    break;
                }
                j--;
            }
            if(value[i].h - value[j].h <= MAXH) {       //保证不会摔死的情况下,寻找最优
                if(j) {     //非地面,所以可能需要走一段路
                    dp[i][0] = min(dp[j][0] + value[i].l - value[j].l, dp[j][1] + value[j].r - value[i].l) + value[i].h - value[j].h;
                }else {
                    dp[i][0] = value[i].h - value[j].h;
                }
            }
            
            //找寻从右端点落下,可以掉落到的平台位置
            j = i - 1;
            while(j >= 0 && value[i].h - value[j].h <= MAXH) {      
                if(value[i].r >= value[j].l && value[i].r <= value[j].r) {
                    break;
                }
                j--;
            }
            if(value[i].h - value[j].h <= MAXH) {       //保证不会摔死的情况下,寻找最优
                 if(j) {                                //下落到非地面的平台上,如果是地面,则无需从端点走向下落点
                     dp[i][1] = min(dp[j][0] + value[i].r - value[j].l, dp[j][1] + value[j].r - value[i].r) + value[i].h - value[j].h;
                }else {
                    dp[i][1] = value[i].h - value[j].h;
                }
            }
        }
        cout << min(dp[N+1][0], dp[N+1][1]) << endl;
    }
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值