Codeforces 505C Mr. Kitayuta, the Treasure Hunter:dp【考慮可用範圍】

Leohh發表於2018-01-04

題目連結: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 }

 

相關文章