http://acm.hdu.edu.cn/showproblem.php?pid=3016
题意: 是男人就下100层。这个男人开始在最顶层,并且拥有100的能量,如果他能下到他下面的横木上,他将无条件获得横木的val。他只能从横木的左端点和右端点下落。要是中途他的能量小于或等于0,he will die~
解题思路:要求到达最后一层的能量最大值,显然要用DP。一开始自然而然能够想到将这些横木按照从大到小排序。然后,就要思考这个男人要怎样下落,因为他只能从左端点或者右端点下落,所以要找到离他当前横木最近的满足条件的横木下落。
这里要用到线段树,当横木按照从大到小的顺序排完序之后,将这些横木插入到线段树中,线段树中装入的值是:tr[i].l , tr[i].r,即这些横木的左端点和右端点, pushup的时候:
tr[i].l = min(tr[i<<1].l,tr[i<<1|1].l);
tr[i].r = max(tr[i<<1].r,tr[i<<1|1].r);
即非叶子节点存当前区间的左端点的最小值和右端点的最大值。具体为什么下面会用到。
然后按照横木从高到低模拟这个男人的运动。当男人位于第i块横木时,在线段树中查找(i+1,n)区间的满足能从左端点下降的最近的横木j,并且满足第i块横木的左端点大于第j块横木的左端点,第i块横木的左端点小于第j块横木的右端点。此时,非叶子节点存当前的左端点的最小值和右端点的最大值就有作用了。什么作用?优化啊,具体见代码;从右端点下降的过程同左端点。
如果能下降到第j块横木,那么dp[j] = max(dp[j],dp[i]+arr[j].val);
如果没有找到能符合条件的,那么 dp[n+1] = max(dp[n+1],dp[i])(我自己假设的第n+1块横木为地板,就相当于直接掉到地板上);
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
#define lson l,mid,i<<1
#define rson mid+1,r,i<<1|1
#define clr(x,y) memset(x,y,sizeof(x))
const int N =100000+10;
struct Node
{
int h,l,r,v;
friend bool operator < (const Node a ,const Node b)
{
return a.h > b.h;
}
}arr[N];
int dp[N];
struct segment
{
int l;//左端点的值
int r;//右端点的值
}tr[N*4];
void built (int l,int r,int i)
{
if(l==r)
{
tr[i].l = arr[l].l;
tr[i].r = arr[l].r;
return;
}
int mid=(l+r)>>1;
built(lson);
built(rson);
tr[i].l = min(tr[i<<1].l,tr[i<<1|1].l);//pushup
tr[i].r = max(tr[i<<1].r,tr[i<<1|1].r);
return;
}
bool flag;
int ans;
void query(int l,int r,int i,int a,int b,int x,int op)
{
if(l>=a&&r<=b)
{
//如果此时的右端点的值小于该节点的l值,那么下面的节点也没有符合条件的点,直接return
//如果此时的左端点的值大于该节点的l值,那么下面的节点也没有符合条件的点,直接return
if(op==0 && tr[i].l<=arr[x].l) ;
else if(op==1 && tr[i].r >=arr[x].r) ;
else return;
}
if(l==r)
{
if(op==0 && tr[i].l<=arr[x].l && tr[i].r >=arr[x].l) flag=true,ans=l;//找到符合的点,return
else if(op==1 && tr[i].r >=arr[x].r && tr[i].l<=arr[x].r) flag=true,ans=l;
return;
}
int mid=(l+r)>>1;
if(mid>=a) query(lson,a,b,x,op);
if(flag) return;
if(mid<b) query(rson,a,b,x,op);
return;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
scanf("%d%d%d%d",&arr[i].h,&arr[i].l,&arr[i].r,&arr[i].v);
sort(arr+1,arr+1+n);
built(1,n,1);
clr(dp,0);
dp[1]=arr[1].v+100;
for(int i=1;i<n;i++)
{
if(dp[i]<=0) continue;
//查找能从左端点下降的横木
ans=0,flag=false;
query(1,n,1,i+1,n,i,0);
if(ans) dp[ans] = max(dp[ans],dp[i]+arr[ans].v);
else dp[n+1] = dp[i];//没有满足条件的,直接掉到地板上
//查找能从右端点下降的横木
ans=0,flag=false;
query(1,n,1,i+1,n,i,1);
if(ans) dp[ans] = max(dp[ans],dp[i]+arr[ans].v);
else dp[n+1] = dp[i];//没有满足条件的,直接掉到地板上
}
int res = max(dp[n+1],dp[n]);
if(res>0) printf("%d\n",res);
else printf("-1\n");
}
return 0;
}