欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

POJ - 1661(Help Jimmy)

程序员文章站 2022-05-29 07:52:01
...

题目描述

POJ - 1661(Help Jimmy)
POJ - 1661(Help Jimmy)

解题思路

非常有意思的动态规划题。首先思考一个问题,如果最小下落路径已知,那么不管是从高处下落,还是从地面向上起跳,只要路径不变,那么路径长也是不会改变的。所以,理解了这点,就可以解决第一个问题,动态规划的起始点与最终状态。由于从起点下落,起点已知,但是下落的路径有多种可能,所以无法确定最终状态,因此我们可以将起点作为最终状态,由地面向起点动规。
第二个问题就是转移的过程:
POJ - 1661(Help Jimmy)
如上图,如果从平台1的左端点下落,则只可能掉落到平台2上,而无法穿过平台2掉落到平台3上。
POJ - 1661(Help Jimmy)如上图,如果从平台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;
}
相关标签: 打卡