传送门:点击打开链接
题意:现在有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; }