Codeforces 505C Mr. Kitayuta, the Treasure Hunter:dp【考虑可用范围】
题目链接:http://codeforces.com/problemset/problem/505/C
题意:
有n个宝石,分别在位置p[i]。(1 <= n,p[i] <= 30000)
初始时你在位置0,第一次走可以往前跳d的距离。
从第二次跳开始,如果前一次跳的距离是x,这一次跳的距离只能是x-1,x,x+1中的一种。
没每跳到一个地方,会获得那里的所有宝石。
问你最多能拿到多少宝石。
题解:
表示状态:
dp[i][j] = max gems
表示初始在位置i,上一次跳的距离为j,在这以及之后所能拿到的最大价值。
找出答案:
ans = dp[d][d]
如何转移:
dp[i][j] = max dp[i+j-1][j-1]
dp[i][j] = max dp[i+j][j]
dp[i][j] = max dp[i+j+1][j+1]
然而O(N^2)过不了……
因为每次跳的距离最多变化1,所以当n=30000时,跳的距离变化小于250,需要用到的j∈[d-250, d+250]。
精确一些就是j∈[d-k, d+k],其中k = ((sqrt(8n+1)-1)/2)+5。
将所有j映射到[0, 2k]的范围内,这样时间和空间就都能满足了。
边界条件:
if(i>maxd) dp[i][j] = 0
maxd是有宝石的最远距离。
当i>maxd时,之后不可能有任何收获。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <math.h> 5 #define MAX_N 30005 6 #define MAX_V 1005 7 #define cal(x) ((x)-d+k) 8 9 using namespace std; 10 11 int n,d,k; 12 int maxd=0; 13 int a[MAX_N]; 14 int dp[MAX_N][MAX_V]; 15 16 int dfs(int i,int j) 17 { 18 if(i>maxd) return 0; 19 if(dp[i][cal(j)]!=-1) return dp[i][cal(j)]; 20 if(j-1>0) dp[i][cal(j)]=max(dp[i][cal(j)],dfs(i+j-1,j-1)+a[i]); 21 dp[i][cal(j)]=max(dp[i][cal(j)],dfs(i+j,j)+a[i]); 22 dp[i][cal(j)]=max(dp[i][cal(j)],dfs(i+j+1,j+1)+a[i]); 23 return dp[i][cal(j)]; 24 } 25 26 int main() 27 { 28 cin>>n>>d; 29 memset(a,0,sizeof(a)); 30 int x; 31 for(int i=1;i<=n;i++) 32 { 33 cin>>x; 34 a[x]++; 35 maxd=max(maxd,x); 36 } 37 k=(int)((sqrt(n*8+1)-1.0)/2.0)+5; 38 memset(dp,-1,sizeof(dp)); 39 cout<<dfs(d,d)<<endl; 40 }