POJ 1661 Help Jimmy(一定能看懂的不太好想的dp)

题目链接:http://poj.org/problem?id=1661

题目大意:

      小明0时刻在空中的一个点,要往下掉,掉到某个板子上之后,要往左和右走,走的速度1米/秒,掉落的速度也是1米/秒。但是跳跃的高度不能超过Max,给小明的初始点和高度,还有每个板子的左右端点和高度。问最少多长时间到地面。

题目思路

       也是搜了别人的题解才明白哈哈。写一份更容易看懂的。到达每个板子都有两个状态,一个是往左走,一个是往右走,那么dp【1005】【2】,dp【i】【j】是板子按照高度排序后到达第 i 个板子后, i 号板子的下一个板子走下下个需要最少花多长时间。也就是在i的下一个板子上最短花了多长时间,j==0就往左走,j==1就往右,一定要清楚dp【i】【j】是在i的下一个要到达的板子上花的时间这样做有个好处就是,可以把起点看作一个长度为0的板子,所以就可以直接状态转移到这个“板子”上,因为结合上边那句话,这个长度为0的板子存的是下一个要到达板子左右走花的最短时间,而这个“板子上”又正好不花时间。这个转移很精妙。

懂了的话可以直接看代码了,下边有递推细节。

       备注: 怎么递推???

假如当前在i号板子,那么从i-1到0号板子遍历,看是否存在i号板子往左跳高度小于Max的能落在的板子上

☝如果没有,判断这个i号板子是否能直接落在地上而且高度小于Max,更新dp【i】【0】=0;(到地面之后不能再移动)

?如果有,就要开始状态转移,因为dp【i】【0】存的是在下一个板子上花的时间,那么可以在纸上画一下,下一个板子可能向左也可能向右,一定一定要注意那个dp【i】【0】存的是i的下一个板子上活动的时间。那么下一个板子可能左走也可能右右。

                dp [ i ] [ 0 ] = min ( dp [ left ] [ 0 ] + C [ i ] . l - C [ left ] . l , dp [ left ] [ 1 ] + C [ left ] . r - C [ i ] . l );

同理,往右走也来一遍上述过程就好了。

#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
struct Point
{
    int l,r,h;
}C[1005];
int dp[1005][2];
bool cmp(Point a,Point b){
return a.h<b.h;
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        int n,x,y,Max;
        cin>>n>>x>>y>>Max;
        C[n].l=x;C[n].r=x;C[n].h=y;
        for(int i=0;i<n;i++){
            cin>>C[i].l>>C[i].r>>C[i].h;
        }
        sort(C,C+n,cmp);
        memset(dp,0x3f,sizeof(dp));
        for(int i=0;i<=n;i++){
            int left=-1,right=-1;
            for(int j=i-1;j>=0;j--){
                if(left==-1&&C[j].l<=C[i].l&&C[j].r>=C[i].l&&C[i].h-C[j].h<=Max){
                    left=j;
                }
                if(right==-1&&C[j].l<=C[i].r&&C[j].r>=C[i].r&&C[i].h-C[j].h<=Max){
                    right=j;
                }
            }
            if(left==-1){
                if(C[i].h<=Max)dp[i][0]=0;
            }
            else{
                dp[i][0]=min(dp[left][0]+C[i].l-C[left].l,dp[left][1]+C[left].r-C[i].l);
            }
            if(right==-1){
                if(C[i].h<=Max)dp[i][1]=0;
            }
            else{
                dp[i][1]=min(dp[right][0]+C[i].r-C[right].l,dp[right][1]+C[right].r-C[i].r);
            }
        }
        cout<<dp[n][0]+y<<endl;
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值