cf#186-C. Mr. Kitayuta, the Treasure Hunter-dp(预推断+offset)

来源:互联网 发布:php json数组 编辑:程序博客网 时间:2024/05/21 20:22

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步,收集到的gemint n,D;cin>>n>>D;int i,j,tmp;memset(dp,-1,sizeof(dp));//初始化为-1for (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;}





  

0 0
原创粉丝点击