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 }
原文地址:https://www.cnblogs.com/Leohh/p/8194380.html