zoukankan      html  css  js  c++  java
  • hdu 3016 Man Down

    线段树 + DP

    题意:一个游戏(做题前可以先玩一下帮助理解)。题意比游戏简易:每个木板都有一个权值,可正可负或0,人在上面,自身能量要加上这个权值(即能量会发生增减)。人一开始有100能量,站在最高的木板上(要加上这个能量的权,所以其实起始能量应该为100+该板能量)。然后人往下跳,跳法有讲究并不像真实游戏那样可以移动,人下落,只能从一块木板的端点垂直下落,中途不能移动,这和实际游戏有区别,但是也使问题简化了。问这个人怎么跳,使在它落到地面的时候,能量最大,如果中途或者到地面能量<=0,或者跳到一个木板上,下面没有木板可以接住自己了,那么游戏结束,输出-1

    分析:

    既然只能垂直下落,而且是落在最近的板上,所以其实下落后处于哪个木板是唯一确定的
    (这里指的唯一确定是当从左端点下落是唯一确定,从右端点下落是唯一,所以从一个木板下落就两种可能)
    另外,这个问题本质是个DP,其实是可以直接DP的,就是从顶部走到底部的一个策略,因为从一个木板走到下面,选择是唯一
    这个DP就很容易想了,可以把每个木板看做一个点,移动看作一条有向边,那么就构成一个有向图,而且是个DAG,要使下落到地面时的能量最高
    就是在这个DAG上找一个最长路。
    但是建图是个问题,点数又太多,而且鉴于这题,没必要显式建图,只要能知道每个木板能移向哪些木板即可
    这题的线段树有什么用处(我们老是做题,知道这题被归为线段树,就死命往线段树想,其实到底为什么是线段树都不知道,就算想出来,也没意思)
    线段树就是用于解决上面的问题,快速确定每个木板可以向那些木板移动
    以当前木板的做端点X为例,从x垂直下落,满足的木板为 XL<=X<=XR ,并且是最接近当前木板的那块
    我们可以换一下思维,看做点X被覆盖了,X是被[XL,XR]覆盖(当然它除了覆盖X还覆盖了很多其他点),那么我们可以换成单点查询
    查询X的时候,发现X被覆盖了,而且是被[XL,XR]这个线段覆盖的,那么我们就返回它是哪条线段即可
    但还有个问题,点X可能不止被一个线段覆盖,而是被多个线段覆盖,那么该选哪个呢?其实这就是我们的最近问题,它下落是落在离他最近的木板上的
    所以虽然X被多个线段覆盖,我们只能要最近的那个,怎么确定最近那个呢?那就是将木板按高度升序排序
    从最矮的木板开始,让它去覆盖它对应的区间,那么从低到高,点X就一定是被最高的而且满足的木板覆盖的
    而且用当前木板去覆盖之前,先查询从它左边或者右边掉下去能到那个木板
    这样做,什么问题都解决了,可能有人会想,这样从低到高地添加木板去覆盖,可行吗,没有错误吗,答案是确定的,没有错误
    这是个值得思考的问题,而且它是个显然的问题(越是显然的又越难说清楚)

    参考了小HH,但是觉得他这题好像写得累赘了,尤其是DP那部分

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 100010
    #define lch(i) ((i)<<1)
    #define rch(i) ((i)<<1|1)
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    
    int n;
    int dp[N];
    struct seg //木板
    {
        int h,l,r,v;
        int ln,rn; //从左端点和右端点掉下去会去到那个木板上
    }s[N];
    struct node //线段树
    {
        int mark,l,r,n;
        int mid(){
            return (l+r)>>1;
        }
    }t[4*N];
    
    int cmp_h(struct seg x ,struct seg y)
    {
        return x.h < y.h;
    }
    
    void build(int l , int r , int rt)
    {
        t[rt].l = l;   t[rt].r = r;
        t[rt].mark = 0; //没有被覆盖
        t[rt].n = -1; 
        if(l == r) return ;
        int mid = t[rt].mid();
        int ll = lch(rt);
        int rr = rch(rt);
        build(l , mid , ll);
        build(mid+1 , r , rr);
    }
    
    int query(int x ,int rt)
    {
        if(t[rt].l == t[rt].r)
            return t[rt].n;
        int mid = t[rt].mid();
        int ll = lch(rt);
        int rr = rch(rt);
        if(t[rt].mark != -1) 
        {
            t[ll].mark = t[rr].mark = t[rt].mark; //传递标记
            t[ll].n = t[rr].n = t[rt].n; //传递信息
            return t[rt].n;
        }
        if(x<=mid) return query(x , ll);
        else       return query(x , rr);
    }
    
    void updata(int l , int r ,int m , int rt)
    {
        if(t[rt].l == l && t[rt].r == r)
        {
            t[rt].mark = 1;
            t[rt].n = m;
            return ;
        }
        int mid = t[rt].mid();
        int ll = lch(rt);
        int rr = rch(rt);
        if(t[rt].mark != -1)
        {
            t[ll].mark = t[rr].mark = t[rt].mark;
            t[ll].n = t[rr].n = t[rt].n;
            t[rt].mark = -1;
        }
        if(r<=mid)     updata(l,r,m,ll);
        else if(l>mid) updata(l,r,m,rr);
        else
        {
            updata(l,mid,m,ll);
            updata(mid+1,r,m,rr);
        }
    }
    
    int main()
    {
        while(scanf("%d",&n)!=EOF)
        {
            n++; s[0].h = 0;  s[0].l = 1;  s[0].r = 100000;  s[0].v = 0;
            for(int i=1; i<n; i++)
                scanf("%d%d%d%d",&s[i].h, &s[i].l, &s[i].r, &s[i].v);
            sort(s,s+n,cmp_h); //按木板升序排序,这样就可以从下到高地添加木板
            build(1,100000,1); //这个大小,不需要离散化,减少点代码和编码复杂度
            for(int i=0; i<n; i++)
            {
                s[i].ln = query(s[i].l , 1); //查询从当前木板做端点掉下去能去哪个木板
                s[i].rn = query(s[i].r , 1); //查询从当前木板做端点掉下去能去哪个木板
                updata(s[i].l , s[i].r , i , 1);
            }
            memset(dp,0,sizeof(dp));
            dp[n-1] = 100 + s[n-1].v;
            for(int i=n-1; i>=0; i--)
            {
                int ln = s[i].ln;
                int rn = s[i].rn;
                dp[ln] = max(dp[ln] , dp[i] + s[ln].v);
                dp[rn] = max(dp[rn] , dp[i] + s[rn].v);
            }
            printf("%d\n",dp[0] = (dp[0]>0?dp[0]:-1) );
        }
        return 0;
    }
  • 相关阅读:
    MFC tab页面中获到其它页面的数据
    sqlite数据库中"Select * From XXX能查到数据,但是Select DISTINCT group From xxx Order By group却查不出来
    关闭程序出现崩溃(exe 已触发了一个断点及未加载ucrtbased.pdb)
    springboot 通用Mapper使用
    springBoot 发布war包
    springCloud Zuul网关
    springboot hystrix turbine 聚合监控
    springBoot Feign Hystrix Dashboard
    springBoot Ribbon Hystrix Dashboard
    springBoot Feign Hystrix
  • 原文地址:https://www.cnblogs.com/scau20110726/p/3073398.html
Copyright © 2011-2022 走看看