题解:在看到这道题的时候最先想到的是用dfs记忆化存储去解,申请了一维的dp数组,但是很明显我们没有办法判断更新,也就是说当以不同的跳度跳到点i的时候,会出现重复++的情况。参考别人的思路我们申请二维的dp数组,记录从i-1到i的距离。但是步长直接开30000存的话肯定是不行的,又发现,其实走过30000之前,步长的变化不会很大,如果步长每次增加1的话,那么最少1+2+...+n=n(n+1)/2>30000,n<250,即步长变化不会超过250.所以第二维保存相对原始步长的改变量,-250~250,开500就够了,这样就不会MLE了。
dp的做法:定义:dp[i][j]表示点i在上一跳度为length=d+j-250时得到的最大宝石数;(之所以这样定义因为跳度只能在d+/-250左右,我们必须保证这一点)
递推关系:dp[to][j+1]=max(dp[to][j+1],dp[i][x]+save[to]);dp[to][j]=max(dp[to][j],dp[i][x]+save[to]);dp[to][j-1]=max(dp[to][j-1],dp[i][x]+save[to]);
附代码:
#include <bits/stdc++.h>
#define MAX_N 30010
using namespace std;
int n,d,mx;
int result;
int dp[MAX_N][500];
int save[MAX_N];
void dfs(int x,int l,int step);
int main()
{
//储存右界
int dmax=0;
scanf("%d%d",&n,&d);
memset(save,0,sizeof(save));
memset(dp,-1,sizeof(dp));
for( int i = 0; i < n; i++ )
{
scanf("%d",&mx);
save[mx]++;
dmax=max(mx,dmax);
}
//第一个点,初始化result
result=save[d];
//从该点进入,该点的上一跳度为d+j-250=d
dp[d][250]=save[d];
//左界为d,右界为dmax
for( int i = d; i <= dmax; i++ )
{
for( int j = 500; j >= 0; j-- )
{
//偏移量在d+/-250以内
int length=d+j-250;
//下面语句保证了从dp[d][]的上一跳度只能是d
if( dp[i][j] == -1 )
continue;
//跳度的下届
if( length <= 0 )
break;
//点的上届
if( i+length-1 > 30000 )
continue;
if( length != 1 )
dp[i+length-1][j-1]=max(dp[i+length-1][j-1],dp[i][j]+save[i+length-1]);
dp[i+length][j]=max(dp[i+length][j],dp[i][j]+save[i+length]);
dp[i+length+1][j+1]=max(dp[i+length+1][j+1],dp[i][j]+save[i+length+1]);
result=max(max(result,dp[i+length-1][j-1]),max(dp[i+length][j],dp[i+length+1][j+1]));
}
}
printf("%d\n",result);
return 0;
}
看到学长还有用记忆化存储的方法,定义:dp[i][j]为点i在上一跳度为d+j-250时得到的最大宝石数。思路是一致的,膜拜一下代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
using namespace std;
#define maxn 30005
#define inf 0x7ffffff
pair <int ,int > p;
int arr[maxn];
int dp[maxn][600];
int n,d;
int num;
int findG(int x,int len);
int main()
{
scanf("%d%d",&n,&d);
memset(arr,0,sizeof(arr));
memset(dp,-1,sizeof(dp));
int tmp ;
num = -inf;
for(int i = 0; i < n; i++)
{
scanf("%d",&tmp);
arr[tmp] ++;
if(num < tmp)
{
num = tmp;
}
}
//0是第一个点findG返回从该点出发能得到的最多宝石数目
int ans = arr[0];
ans += findG(d,d);
printf("%d\n",ans);
return 0;
}
int findG(int x,int len)
{
//跳度下界
if(len <= 0)
{
return 0;
}
//点上界
if(x > num)
{
return 0;
}
//记忆化存储
if(dp[x][len - d + 300] != -1)
{
return dp[x][len - d + 300];
}
int left = findG(x+len-1,len-1);
int mid = findG(x+len,len);
int right = findG(x+len+1,len+1);
return dp[x][len - d + 300] = arr[x] + max(mid,max(left,right));
}