C——线段树+DP
Description
有n个木桩依次排列,第i个木桩的高度为hi,其上的果冻数量为xi。
开始的时候可以选择站在任意一个木桩上,每次跳跃不限长度而且只能从左向右跳跃,但只能跳到高度与当前所站高度差绝对值小于等于m的柱子上。
问最多能拿到多少个果冻。
最终不一定要落在最后一个木桩上。
思路
由于题目中提示异常明显:只能从左向右跳跃,因此这道题显然为DP题。
设fi表示到了高度i能获得的最大果冻数。考虑任意木桩i,能转移到f[h[i]]的状态范围为f[h[i]-m~h[i]+m],我们显然要从这其中选择最大的值进行转移。
考虑用线段树维护最大值f[k],转移方程为f[h[i]]=max(f[h[i]],f[k]+x[i]),转移后的值再插入线段树中更新当前最大值。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int N=1e7;
int n,m,h[N],a[N],f[N],tree[N],ans,maxn;//f[i]表示高度为i时最多的果冻数量
void update(int k,int x,int y,int p,int val)//单点修改
{
if(x==y)
{
tree[k]=val;
return;
}
int mid=(x+y)>>1;
if(p<=mid) update(k<<1,x,mid,p,val);
else update(k<<1|1,mid+1,y,p,val);
tree[k]=max(tree[k<<1],tree[k<<1|1]);
}
int query(int k,int x,int y,int a,int b)//区间查询最大值
{
if(x==a&&y==b) return tree[k];
int mid=(x+y)>>1;
if(b<=mid) return query(k<<1,x,mid,a,b);
else if(a>mid) return query(k<<1|1,mid+1,y,a,b);
else return max(query(k<<1,x,mid,a,mid),query(k<<1|1,mid+1,y,mid+1,b));
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%d",&h[i],&a[i]),maxn=max(maxn,h[i]);
maxn+=m;//高度的最大值为最大的h[i]+m
for(int i=1;i<=n;i++)
{
//查询可以转移到f[h[i]的最大的状态
f[h[i]]=max(f[h[i]],query(1,0,maxn,max(0,h[i]-m),h[i]+m)+a[i]);
//更新h[i]位置上的数据
update(1,0,maxn,h[i],f[h[i]]);
}
for(int i=0;i<=maxn;i++)
ans=max(ans,f[i]);
printf("%d",ans);
return 0;
}