http://codeforces.com/problemset/problem/505/C
题意:
有30001个岛屿,下标为0~30000,给n个数告诉你有n份宝藏藏在哪些岛上。
你从0往下标大的方向跳,第一步跳的距离为d。
如果上一步跳的距离为D,这一步就可以跳D-1或D或D+1(但是距离必须大于0)。
问最多拿到多少宝藏。
思路:
dp[i][j] 表示 刚到达第i岛屿,从上一岛屿跳过来的步长是D+j单位距离 ,
那么我们可以推得三种后继情况
int dis=i+D+j,
1· 这一步选择仍跳D+j距离,则dp[dis][j]=max(dp[dis][j],dp[i][j]+vis[dis]);
2· 这一步选择跳D+j+1距离,则dp[dis+1][j+1]=max(dp[dis+1][j+1],dp[i][j]+vis[dis+1]);
3· 这一步选择跳D+j-1距离,则dp[dis-1][j-1]=max(itself,dp[i][j]+vis[dis-1]);
注意每一步要判断即将到达的位置是否<=30000
初始化dp数组为-1,要递推的话肯定是从第0个岛屿推起,也就是初始化dp[D][0]=vis[D] // 其实是dp[D][250]=vis[250];
因为len=30000,并且每次步长最多递增1,1+2+3+...+n=n*(n+1)/2<=30000,
n最多不超过250, 也就是最长的步长不会超过D+250,
当然如果每次递减1,最短的步长也不会少于D-250
所以我们枚举的 的j,只需要从-250到250,显然数组下标为负数不方便,我们加250的offset,把j变成0~500,
而每次 从上一岛屿跳过来的步长就变成了D+j-250
在递推过程中,第三种情况要注意加判断,因为第三种是步长不断减小,可能减成负数了。。。我们令dis-1>i,即距离差为正即可
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <set> #include <vector> #include <iostream> using namespace std; const double pi=acos(-1.0); double eps=0.000001; int max(int a,int b) {return a<b?b:a;} int max(int a,int b,int c) {return max(a,max(b,c));} int vis[30005]; int dp[30005][520]; int main() { //vis[i] //第i岛屿的gem数 //dp[i][j] //到达第i处, 此时走j步,收集到的gem int n,D; cin>>n>>D; int i,j,tmp; memset(dp,-1,sizeof(dp)); //初始化为-1 for (i=1;i<=n;i++) { scanf("%d",&tmp); vis[tmp]++; } int maxx=vis[D]; dp[D][250]=vis[D]; for (i=D;i<=30000;i++) { for (j=1;j<=500;j++) { if (dp[i][j]==-1)continue; int dis=i+D+j-250; if (dis<=30000) { dp[dis][j]=max(dp[dis][j],dp[i][j]+vis[dis]); maxx=max(maxx,dp[dis][j]); } if (dis+1<=30000) { dp[dis+1][j+1]=max(dp[dis+1][j+1],dp[i][j]+vis[dis+1]); maxx=max(maxx,dp[dis+1][j+1]); } if (dis-1>i&&dis-1<=30000)//要求步长为正数,即dis-1>i { dp[dis-1][j-1]=max(dp[dis-1][j-1],dp[i][j]+vis[dis-1]); maxx=max(maxx,dp[dis-1][j-1]); } } } printf("%d\n",maxx); return 0; }