题意:给你n个柱子,每个柱子都有一个高度,你从第一个柱子开始跳往后跳,后面的柱子要满足|hi-hj|>=d才可以跳上去,问最多跳多少步,并且输出一种跳法,从小到大输出柱子号。
思路:真肯定是个dp,而且思路很简单//转移方程 dp[i]=max(dp[j])+1,(|hi-hj|>=d)
但是n的范围是10^5,那么你每次都对之前的柱子扫一边找最大就不行了,那样就是n^2的算法,所以要对每次找最大进行优化,这里优化的方式就是应用线段树,而输出路径就用递归就行,由于没有怎么输出过路径导致不太会。
线段树优化的道理与正确性:
一开始不知道怎么优化,光想着对下标建树,怎么想怎么不对,后来找了一份别人的代码一看原来是对高度建树,因为你要找的是符合这个条件
(|hi-hj|>=d)的dp[j],所以你可以把所有i之前的dp[j]都存在相应高度的线段树端点处(正因为这个才更应该用线段树),然后每次query(h[i]+d~maxn
)与(1~h[i]-d)的最大的dp[j],然后再来一次单点更新,把a[i]点的值改为dp[j]+1.
另外需要考虑的机试h的范围为10^15,你直接建线段树肯定是建不下的,所以要对高度进行离散化,然后对离散化后的高度个数建树,然后对每个a[i]进行二分查找,找到它在离散化数组中的下标,而且还有小于h[i]-d的第一个数的下标与大于h[i]+d第一个数的小标,都要用二分查找,具体怎么写的看代码。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stdlib.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
using namespace std;
const int maxn=100010;
//转移方程 dp[i]=max(dp[j])+1,(|hi-hj|>=d)
ll mx[maxn<<2],pos[maxn<<2],pre[maxn];
ll a[maxn],b[maxn];
ll n,d;
int cur;
ll maxlen,maxpos;
ll best;
void pushup(int rt)
{
if(mx[rt<<1]>mx[rt<<1|1])
{
mx[rt]=mx[rt<<1];
pos[rt]=pos[rt<<1];
}
else
{
mx[rt]=mx[rt<<1|1];
pos[rt]=pos[rt<<1|1];
}
}
void update(int q,int c,int i,int l,int r,int rt)
{
if(l==r)
{
if(mx[rt]<c)
{
mx[rt]=c;
pos[rt]=i;
}
return;
}
int mid=(l+r)>>1;
if(q<=mid) update(q,c,i,lson);
else update(q,c,i,rson);
pushup(rt);
}
void query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
if(mx[rt]>maxlen)
{
maxlen=mx[rt];
maxpos=pos[rt];
}
return;
}
int mid=(l+r)>>1;
if(L<=mid) query(L,R,lson);
if(mid<R) query(L,R,rson);
}
void print(int m)
{
if(pre[m])
{
print(pre[m]);
printf("%d ",pre[m]);
}
}
void init()
{
memset(mx,0,sizeof(mx));
memset(pos,0,sizeof(pos));
memset(pre,0,sizeof(pre));
cur=1;
}
int findl(long long num) {//查找比num小的第一个值,因为num不一定在b这个数组里
int l = 1, r = cur;
while(l < r) {
int mid = (l + r + 1) / 2;
if (b[mid] <= num) {
l = mid;
} else {
r = mid - 1;
}
}
return r;
}
int findr(long long num) {//查找比num大的第一个值
int l = 1, r = cur;
while(l < r) {
int mid = (l + r) / 2;
if (b[mid] >= num) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
int main()
{
cin>>n>>d;
init();
for(int i=1; i<=n; i++)
{
cin>>a[i];
b[i]=a[i];
}
sort(b+1,b+n+1);
for(int i=2; i<=n; i++)
{
if(b[i]!=b[i-1])
{
b[++cur]=b[i];
}
}
update(findl(a[1]),1,1,1,cur,1);
int best=1;
int len=1;
pre[1]=0;
for(int i=2; i<=n; i++)
{
maxlen=0; maxpos=0;
int L=findl(a[i]-d);
int R=findr(a[i]+d);
// cout<<L<<" "<<R<<endl;
if(b[L]+d<=a[i]){
query(1,L,1,cur,1);
}
if(a[i]+d<=b[R]){
query(R,cur,1,cur,1);
}
update(findl(a[i]),maxlen+1,i,1,cur,1);
// cout<<maxpos<<" "<<maxlen<<endl;
pre[i] = maxpos;
if(maxlen+ 1>len)
{
len = maxlen+ 1;
best= i;
}
}
printf("%d\n",len);
print(best);
cout<<best<<endl;
return 0;
}