dp Codeforces505C Mr. Kitayuta, the Treasure Hunter

传送门:点击打开链接

题意:现在有3w个点,最开始站在第0个点上,第一次会向右跳d个点。

若上一次为d,这次只能向右跳d-1,d,和d+1步。步数不能为0,当不能向右跳时即停止。

思路:不考虑时间和空间,最朴素的方法就是设dp[i][j]表示上一步跳了j步,当前在i点上,之后的转移也很好写

然而这明显是会爆时间爆空间的,不过我们可以发现,二维的j并不需要等于30000,而只会在[d-245,d+245]这个范围内!

因为,最差情况下,它的步数逐渐增大,那么会形成等差数列,所以x*(x-1)/2<=d,可以很容易得到x的变化并不会太大。

在上面我们证明了复杂度,所以接下来只要直接用记忆化搜索就能写完了,不过有个地方要注意,cf上的G++默认的栈是满的,但是codeblocks的栈不是满的,所以在本地测试会爆栈,不过提交的时候是不会爆的。

  

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;

const int MX = 3e4 + 5;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

const int P = 250;
int dp[MX][500], val[MX], n, d;
int DP(int p, int b) {
    if(dp[p][P + b] != -1) return dp[p][P + b];
    int ret = 0;
    for(int i = -1; i <= 1; i++) {
        if(d + b + i < 1 || p + d + b + i > 30000) continue;
        ret = max(ret, DP(p + d + b + i, b + i));
    }
    return dp[p][P + b] = ret + val[p];
}
int main() {
    //FIN;
    while(~scanf("%d%d", &n, &d)) {
        memset(dp, -1, sizeof(dp));
        memset(val, 0, sizeof(val));
        for(int i = 1; i <= n; i++) {
            int t; scanf("%d", &t);
            val[t]++;
        }
        printf("%d\n", DP(d, 0));
    }
    return 0;
}

你可能感兴趣的:(dp Codeforces505C Mr. Kitayuta, the Treasure Hunter)