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