题意:
有编号为0到30000的小岛,总共30001个。
给n和d,表示有n颗宝石,你的初始跳跃长度为d
然后给出n个数p(i),表示p(i)位置有一颗宝石
开始你在岛0上,第一次跳跃只能跳过d个小岛
假设上一次你的跳跃距离为x,那么这一次你可以选择跳x、x+1、x-1,(要求跳x-1的时候x-1>=1,不能跳过30000)
当你不能再继续跳的时候停止,跳到一个带有宝石的岛上会收集岛上的宝石
现在问过程中最多收集到多少个宝石
数据范围:n<=3e4,d<=3e4,d<=p(i)<=3e4
解法:
朴素dp:
令d[i][j]为当前跳到i,上一次跳跃距离为j的最大宝石数量
但是题目的数据范围是3e4的,二维的数组看不下,且O(3e4^2)的算法跑不动
但其实第二维的跳跃范围没有3e4这么大
假设第一次跳d,第二次跳d+1,第三次跳d+2,这样跳跃距离不断增加
因为小岛只有3e4个,设总共跳了x次,第x次时距离比初始情况增加(x-1)
那么可以计算出x*d+(x-1)*x/2<=3e4
要使得距离增加量最大,不妨令d为1,那么x+(x-1)*x/2<=3e4
稍微放大一下近似为x*x/2<=3e4,即x*x<=6e4,显然x<300
因此跳完全程距离的偏转值不会超过300,
即开始跳跃距离为d,全程的距离肯定在(d-300,d+300)之间
因此第二维只需要开600大小就行了
总复杂度也变为了O(3e4*600)
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=3e4+5;
const int N=30000;
int f[maxm][605];
int cnt[maxm];
int n,d;
signed main(){
cin>>n>>d;
for(int i=1;i<=n;i++){
int x;cin>>x;
cnt[x]++;
}
//将长度x偏移为x-d+300
memset(f,-1,sizeof f);
f[d][d-d+300]=cnt[d];
for(int i=d;i<=N;i++){
for(int j=0;j<=600;j++){
if(f[i][j]==-1)continue;//无法到达的跳过
int x=j+d-300;//真实长度
if(i+x+0>i&&i+x+0<=N)f[i+x+0][j+0]=max(f[i+x+0][j+0],f[i][j]+cnt[i+x+0]);
if(i+x+1>i&&i+x+1<=N)f[i+x+1][j+1]=max(f[i+x+1][j+1],f[i][j]+cnt[i+x+1]);
if(i+x-1>i&&i+x-1<=N)f[i+x-1][j-1]=max(f[i+x-1][j-1],f[i][j]+cnt[i+x-1]);
}
}
//
int ans=0;
for(int i=d;i<=N;i++){
for(int j=0;j<=600;j++){
ans=max(ans,f[i][j]);
}
}
cout<<ans<<endl;
return 0;
}