HDU 3016 Man Down

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3016


题意:有n块木板,每一块木板有不同的高度h和左右端点的坐标xl和xr,从最高的木板下落,仅可以从木板的2个端点垂直下落,也就是当下落xi端点在下一块木板的[xl,xr]中才可以到达该木板,每一块木板有权值,初始值为100当初始值为0时死亡,问最好的情况到达地面(h=0)的时候权值最大


思路:一开始就往线段树去想因为是线段树专题嘛(我承认我看题解了)……但是本质上来说这是一道dp的题目,每到达一块木板有2种选择在左端点下落或者在右端点下落,但是如何得知下落之后的下一块木板了,由于数据量的问题,暴力处理的话会超时,这个时候就应该使用线段树来进行辅助优化,将木板由低到高进行排序,分别查询2个端点所在的区间被那一段线段所覆盖,则可以得知从该端点下落到达那一个木板,在用当前木板维护线段树就可以了。

因为dp的时候初始化为0,所以当权值为负数不需要在进行处理,并不是允许下落的时候出现权值为负数的情况。

在这里再阐述一下我对线段树的理解,它作为一种数据结构,是以一定的逻辑结构来保存处理数据,而不是作为一种算法提供输入端口输出端口吐出处理结果,像区间更新一类的操作线段树可以很好的辅助完成


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100030
using namespace std;

struct Tree
{
    int l,r,date;
}tree[maxn*3];

struct Node
{
    int l,r,h,val,lnxt,rnxt;
}s[maxn];

int lazy[maxn*3],dp[maxn];

bool cmp(Node p,Node q)
{
    return p.h<q.h;
}

void Pushdown(int root)
{
    if (lazy[root]!=-1)
    {
        lazy[root<<1]=lazy[root];
        lazy[root<<1|1]=lazy[root];
        tree[root<<1].date=tree[root].date;
        tree[root<<1|1].date=tree[root].date;
        lazy[root]=-1;
    }
}

void build(int root,int l,int r)
{
    tree[root].l=l;
    tree[root].r=r;
    tree[root].date=-1;
    if (l==r) return;

    int mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);

}

void update(int root,int l,int r,int val)
{
    if (tree[root].l>=l && tree[root].r<=r)
    {
        lazy[root]=val;
        tree[root].date=val;
        return;
    }

    Pushdown(root);
    int mid=(tree[root].l+tree[root].r)>>1;//cout<<root<<":"<<mid<<":"<<r<<endl;
    if (l<=mid) update(root<<1,l,r,val);
    if (r>mid) update(root<<1|1,l,r,val);
}

int que(int root,int gold)
{
    if (tree[root].l==tree[root].r) return tree[root].date;

    Pushdown(root);
    int mid=(tree[root].l+tree[root].r)>>1;
    if (gold<=mid) return que(root<<1,gold);
    else if (gold>mid) return que(root<<1|1,gold);
}

void init()
{
    memset(lazy,-1,sizeof(lazy));
    memset(dp,0,sizeof(dp));
    build(1,1,maxn);
    s[0].l=1;
    s[0].r=maxn;
    s[0].h=0;
    s[0].val=0;
}

int main()
{
    int n;
    while (scanf("%d",&n)!=EOF)
    {
        init();
        for (int i=1;i<=n;i++)
        {
            scanf("%d%d%d%d",&s[i].h,&s[i].l,&s[i].r,&s[i].val);
        }
        sort(s,s+n+1,cmp);

        for (int i=0;i<=n;i++)
        {
            s[i].lnxt=que(1,s[i].l);
            s[i].rnxt=que(1,s[i].r);
            update(1,s[i].l,s[i].r,i);
        }
         dp[n]=100+s[n].val;
        for (int i=n;i>=0;i--)
        {
            int ll=s[i].lnxt,rr=s[i].rnxt;

            dp[ll]=max(dp[ll],dp[i]+s[ll].val);
            dp[rr]=max(dp[rr],dp[i]+s[rr].val);
        }

        if (dp[0]<=0) printf("-1\n");
        else printf("%d\n",dp[0]);
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值