题目

Shuseki 群岛是 Yutampo 海中 30001 个小岛的群岛。这些岛沿一条线均匀分布,从西到东编号从 0 到 30000。
这些岛屿拥有许多宝藏。在 Shuseki 群岛***有 颗宝石,而第 颗宝石位于 岛上。(

Kitayuta 先生刚到达 0 岛。凭借出色的跳跃能力,他将按照以下过程向东部的岛屿反复跳跃:

  • 首先,他将从岛 0 跳到岛 。(
  • 之后,他将根据以下规则继续跳跃。
    为前一次跳跃的长度,即,如果他的前一次跳跃是从岛 到岛 ,则令
    他将向东进行长度为 的跳跃。即他可以跳至岛 (如果存在)。
    跳跃的长度必须为正数,即当 时,他不能执行长度为 0 的跳跃。
    如果没有有效的目的地,他将停止跳跃。

Kitayuta 先生将在此过程中到达过的岛屿上收集宝石。找到他可以收集的最大宝石数量。

解题思路

动态规划

表示前一次跳跃的长度是 ,到达岛屿 时收集的最大宝石数量。
那么,下一次跳跃长度 这 3 中情况。
所以状态转移方程是
当然前提是 K 先生可以到达岛屿 ,即 。其中 表示岛屿 中宝石的数量。

假设第一步跳 1,后面每次增加 1 就可以求出最大的跳跃步长,计算 ,解得跳跃次数 ,所以最大跳不到 250 次,即跳跃长度最多在 的基础上增加 1 的次数不超过 250。
同理,假设第一步跳 249,后面每次减 1,到跳跃长度为 1 时已跳出整个群岛。因此, 的取值范围是

是数组的下标,不能取负值,所以给 一个偏移量 250,这样, 的取值范围就是
原来 表示前一次跳跃长度是 ,到达岛屿 时收集的最大宝石数量;给了偏移量后, 才是原来 表示的意思。

C++代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn = 30001;

int main(){
    int n, d;
    cin >> n >> d;
    vector<int> gems(maxn, 0);
    int pos;
    for(int i=0; i<n; ++i){
        cin >> pos;
        ++gems[pos];
    }

    int ans = gems[d];
    vector<vector<int>> dp(maxn, vector<int>(505, -1));
    dp[d][250] = ans;
    for(int i=d; i<maxn; ++i){
        for(int j=1; j<=500; ++j){
            if(dp[i][j]==-1) continue;
            pos = i + d + j - 250;
            for(int k=-1; k<=1; ++k){
                int next = pos + k;
                if(next>i && next<maxn){
                    dp[next][j+k] = max(dp[next][j+k], dp[i][j] + gems[next]);
                    ans = max(ans, dp[next][j+k]);
                }
            }
        }
    }

    cout << ans << endl;
    return 0;
}