cf505 C. Mr. Kitayuta, the Treasure Hunter(dp)

题意:

整数线段范围为 \([0,30000]\)。有 \(n\) 个宝藏分布在一些整点上,位置已知。现从0出发往右跳到d处,接下来每一次可以往右跳上一次跳的步数t或者t-1步或者t+1步。跳到某个位置就能拿走那个位置的全部宝藏。不能跳0步;跳出界就结束。求最多能拿走几个宝藏。

\(1\le n,d\le 30000\)

思路:

首先考虑一种dp:\(f(i,j)\) 表示在位置 \(i\) 且上一次跳了 \(j\) 步的最大价值。那么下一步能走到 \(i+j-1/i+j/i+j+1\)

从右往左更新,那么 \(f(i,j)=i处宝藏数+\max\{ f(i+j-1,j-1),f(i+j,j),f(i+j+1,j+1) \}\)

答案是 \(f(d,d)\),即当前位置为 \(d\),上一次跳了 \(d\)

下面缩小 \(j\) 的范围。上界:每次都多走一步,\(d+(d+1)+(d+2)+\cdots +(d+245)\ge 0+1+2+\cdots +245 = 30135>30000\);下界:每次都少走一步,\(d+(d-1)+(d-2)+\cdots +(d-245) \ge 245 + 244 + \cdots + 1=30135>30000\)

所以 \(j\in [d-245,d+245]\)。把 \(j\) 减去 \(d\) 再加上 \(250\) 之后存进数组。

下面是记忆化搜索的写法

const int N = 30001, D = 250;
int n, d, a[N], f[N][D*2+3];

int dp(int i, int j)
{
    if(j == 0 || i >= N) return 0; //越界
    int &v = f[i][j-d+D]; //平移区间
    if(v == -1) v = a[i] +
        max({dp(i + j-1, j-1), dp(i + j, j), dp(i + j+1, j+1)});
    return v;
}

signed main()
{
    scanf("%d%d", &n, &d);
    while(n--)
    {
        int x; scanf("%d", &x); //宝藏位置
        a[x]++;
    }

    memset(f, -1, sizeof f);

    printf("%d", dp(d, d));
}

posted @ 2022-02-10 00:09  Bellala  阅读(51)  评论(0)    收藏  举报