poj1661 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

我个人认为这题目就是加强版的数字三角形,因为在这道题中,如果我们从顶向下选择路径,当前最优解不一定是全局的最优解,我们更应该采用自底向上的方法去做,从下面开始往上搜索路径,和数字三角形一样,都是有两个方向,不过那题是取最大值,而我们这题就取最小值,另外不同的是,这道题会有两个入口,数字三角形其实是单单只是会有一个入口(这个可能比较难形容,可以去画画图)。

那么讲完相似之处,我们就用这种思想去dp。首先对于每一块板我们都有左端点和右端点,那么我们把来到第i块板的最小距离分解成两个子问题,就是从左边进入这块板的最小值,和从右边进入这块板的最小值,分别用dp[i][0],dp[i][1]去表示。对于这道题我们先预处理一下,先把大地当成最后一块板,把起点当成第一块板(左右端点重合的区间),把板从低到高排好序,然后对每一块进行dp.

转移方程:

 dp[i][0]=c[i].h-c[k].h+min(dp[k][0]+c[i].a-c[k].a,dp[k][1]+c[k].b-c[i].a);
c[i].h-c[k].h//这是高度差

min(dp[k][0]+c[i].a-c[k].a,dp[k][1]+c[k].b-c[i].a)//这是对于第k块板,从大地到左边进入,和到从右边进入的最小值,再加上它们到左端点的距离

然后dp[i][0]就出来了,同理dp[i][1]也是一样。那么这道题需要注意的几个点,一是要判断到底能不能跳到左右端点,而是判断如果找到一个点k和点i的值相差大于MAX,那么从这个端点进入就是永远不可能了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
struct node
{
    int a;
    int b;
    int h;
} c[1009];
int cmp(node a,node b)
{
    return a.h<b.h;
}
int dp[1009][2];
int n,MAX;
void lmove(int i)//从第k板从左进入第i块板
{
    int k;
    k=i-1;
    while(k!=0&&c[i].h-c[k].h<=MAX)//保证下一块板不是地,且差距比MAX要小
    {
        if(c[k].a<=c[i].a&&c[i].a<=c[k].b)
        {
            dp[i][0]=c[i].h-c[k].h+min(dp[k][0]+c[i].a-c[k].a,dp[k][1]+c[k].b-c[i].a);
            return ;//如果找到第一个符合条件的,那么就可以直接退出了
            //因为条件符合了,我们只能从这个第k块板跳到第i块板上面去。
            //如果下面还有板,那么我们要么会被这块板阻挡,要么我们从这块板跳到下面的板距离一样
        }
        else
        {
            k--;//虽然符合不超过MAX的条件,但是不能从左边跳上去
        }
    }
    if(c[i].h-c[k].h>MAX)
    {
        dp[i][0]=99999999;//如果没有找到一块板符合的
        //那么也就是说,没有一块板可以从左边跳到第i块板
    }
    if(k==0)
    {
        dp[i][0]=c[i].h-c[k].h;
    }
}
void rmove(int i)
{
    int k=i-1;
    while(k!=0&&c[i].h-c[k].h<=MAX)
    {
        if(c[k].a<=c[i].b&&c[i].b<=c[k].b)
        {
            dp[i][1]=c[i].h-c[k].h+min(dp[k][0]+c[i].b-c[k].a,dp[k][1]+c[k].b-c[i].b);
            return ;
        }
        else k--;
    }
    if(c[i].h-c[k].h>MAX)
    {
        dp[i][1]=99999999;//如果没有找到一块板符合的
        //那么也就是说,没有一块板可以从左边跳到第i块板
    }
    if(k==0)
    {
        dp[i][1]=c[i].h-c[k].h;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int i;
        memset(dp,0,sizeof(dp));
        scanf("%d",&n);
        scanf("%d%d%d",&c[n+1].a,&c[n+1].h,&MAX);
        c[n+1].b=c[n+1].a;
        for(i=1; i<=n; i++)
        {
            scanf("%d%d%d",&c[i].a,&c[i].b,&c[i].h);
        }
        c[0].a=-20000;
        c[0].b=20000;
        c[0].h=0;
        
        sort(c,c+n+1,cmp);//高度从小到大排序
        for(i=1; i<=n+1; i++) //自底向上dp
        {
            lmove(i);
            rmove(i);
        }
        //dp[i][0]表示第i块板从左进入的最小值
        //dp[i][1]表示第i块板从右进入的最小值
        printf("%d\n",min(dp[n+1][0],dp[n+1][1]));
    }
}


猜你喜欢

转载自blog.csdn.net/keepcoral/article/details/80115173
今日推荐