POJ - 1661 DP

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
Source

POJ Monthly–2004.05.15 CEOI 2000

解题思路:

这道题写着比较繁琐,但是思路其实并不算太复杂,因为从上面掉下来只有两种情况,要么从左边下来,要么从右边下来,而从高处下来的话只要事先知道比它低的所有的最小值了,那么自然可以求出相应位置的最小值,这里用dp[0][i]代表从第i块高的板左边落地的最小时间,相应的dp[1][i]代表从右边落地的最短时间,转移方程为:
从左边走:
d p [ 0 ] [ i ] = m i n ( d p [ 0 ] [ i ] , m i n ( d p [ 0 ] [ j ] + n u m [ i ] . l n u m [ j ] . l , d p [ 1 ] [ j ] + n u m [ j ] . r n u m [ i ] . l ) + n u m [ i ] . h n u m [ j ] . h ) .
从右边走:
d p [ 1 ] [ i ] = m i n ( d p [ 1 ] [ i ] , m i n ( d p [ 0 ] [ j ] + n u m [ i ] . r n u m [ j ] . l , d p [ 1 ] [ j ] + n u m [ j ] . r n u m [ i ] . r ) + n u m [ i ] . h n u m [ j ] . h ) .

AC代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1000;
const int INF = 1e9 + 7;
struct NOOD {
    int l, r, h;
}num[maxn + 5];
int dp[2][maxn + 5];
int n, Max, x, h;
int T;
bool cmp(NOOD x, NOOD y) {
    return x.h < y.h;
}
void Init() {
    scanf("%d%d%d%d", &n, &x, &h, &Max);
    for(int i = 1; i <= n; i++) {
        scanf("%d%d%d", &num[i].l, &num[i].r, &num[i].h);
    }
    num[0].l = -20000;
    num[0].r = 20000;
    num[0].h = 0;
    num[++n].l = num[n].r = x;
    num[n].h = h;
    sort(num, num + n + 1, cmp);
    for(int i = 1; i <= n; i++)dp[0][i] = dp[1][i] = INF;
    dp[0][0] = 0;
    dp[1][0] = 0;
}
int main() {
    scanf("%d", &T);
    while(T--) {
        Init();
        for(int i = 1; i <= n; i++) {
            int flag1 = 0;
            int flag2 = 0;
            for(int j = i - 1; j >= 0; j--) {
                if(flag1 && flag2)break;//因为是从高处到低处的,所以左边右边都已经有过就不会再到下面了
                if(num[i].l <= num[j].r && num[i].l >= num[j].l && !flag1) {//从左端点下去时,判断能否到达上面
                    flag1 = 1;
                    if(num[i].h - num[j].h <= Max) {//要不能被摔死
                        if(j != 0)dp[0][i] = min(dp[0][i], min(dp[0][j] + num[i].l - num[j].l, dp[1][j] + num[j].r - num[i].l) + num[i].h - num[j].h);
                        else dp[0][i] = min(dp[0][i], num[i].h);
                    }
                }
                if(num[i].r >= num[j].l && num[i].r <= num[j].r && !flag2){//从右端点下去时,判断能否到达上面
                    flag2 = 1;
                    if(num[i].h - num[j].h <= Max) {
                        if(j != 0)dp[1][i] = min(dp[1][i], min(dp[0][j] + num[i].r - num[j].l, dp[1][j] + num[j].r - num[i].r) + num[i].h - num[j].h);
                        else dp[1][i] = min(dp[1][i], num[i].h);
                    }
                }
            }
        }
        printf("%d\n", min(dp[0][n], dp[1][n]));//最后输出从左边和右边落地的最小时间
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36889101/article/details/80138934