题目链接
The Shuseki Islands are an archipelago of 30001 small islands in the Yutampo Sea. The islands are evenly spaced along a line, numbered from 0 to 30000 from the west to the east. These islands are known to contain many treasures. There are n gems in the Shuseki Islands in total, and the i-th gem is located on island pi.
Mr. Kitayuta has just arrived at island 0. With his great jumping ability, he will repeatedly perform jumps between islands to the east according to the following process:
Mr. Kitayuta will collect the gems on the islands visited during the process. Find the maximum number of gems that he can collect.
The first line of the input contains two space-separated integers n and d (1 ≤ n, d ≤ 30000), denoting the number of the gems in the Shuseki Islands and the length of the Mr. Kitayuta's first jump, respectively.
The next n lines describe the location of the gems. The i-th of them (1 ≤ i ≤ n) contains a integer pi (d ≤ p1 ≤ p2 ≤ ... ≤ pn ≤ 30000), denoting the number of the island that contains the i-th gem.
Print the maximum number of gems that Mr. Kitayuta can collect.
4 10 10 21 27 27
3
8 8 9 19 28 36 45 55 66 78
6
13 7 8 8 9 16 17 17 18 21 23 24 24 26 30
4
题意:有30001个岛,在一条线上,从左到右编号一次为0到30000。某些岛屿上有些宝石。初始的时候有个人在岛屿0,他将跳到岛屿d,他跳跃的距离为d。如果当前他跳跃的距离为L,他下一次跳跃的距离只能为L-1,L,L+1之一且不能为0。他只能往编号更大的岛跳,直到他不能跳,问他最多能收集多少个宝石?
题解:很容易想到dp,用dp[i][j] 表示当前在岛屿i,上一次跳跃的距离为j,最多能收集多少个宝石。但是这样无论空间还是时间都不能接受。我们可以发现,他跳跃的距离L不会超过d+250,因为1+2+3+...250>30000。有了这个性质,我们就可以用dp[i][j]表示当前在岛屿i,上一次跳跃的距离为d+j,该状态下他最多能收集多少个宝石?
空间复杂度和时间复杂度都为O(n*sqrt(n))。
代码如下:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> #include<string> #include<queue> #include<stack> #include<map> #include<set> #include<stdlib.h> #include<vector> #define inff 0x3fffffff #define nn 110000 #define mod 1000000007 typedef long long LL; const LL inf64=inff*(LL)inff; using namespace std; int n,d; int num[31000]; int dp[31000][510]; int main() { int i,x,j; while(scanf("%d%d",&n,&d)!=EOF) { memset(num,0,sizeof(num)); for(i=1;i<=n;i++) { scanf("%d",&x); num[x]++; } memset(dp,-1,sizeof(dp)); dp[d][250]=num[d]; int ans=0; for(i=d;i<=30000;i++) { for(j=0;j<=500;j++) { if(dp[i][j]!=-1) { ans=max(dp[i][j],ans); if(i+d+j-250<=30000) dp[i+d+j-250][j]=max(dp[i+d+j-250][j],dp[i][j]+num[i+d+j-250]); if(i+d+j-1-250<=30000&&d+j-250-1) dp[i+d+j-251][j-1]=max(dp[i+d+j-251][j-1],dp[i][j]+num[i+d+j-251]); if(i+d+j+1-250<=30000) dp[i+d+j+1-250][j+1]=max(dp[i+d+j+1-250][j+1],dp[i][j]+num[i+d+j+1-250]); } } } printf("%d\n",ans); } return 0; }比赛的时候我没有想到正解,但是知道有效状态数不会太多,所以使用记忆化搜索写过的,当然还要到hash。
复杂度为O(10^7),代码如下:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> #include<string> #include<queue> #include<stack> #include<map> #include<set> #include<stdlib.h> #include<vector> #define inff 0x3fffffff #define nn 21000 #define mod 20000009 typedef long long LL; const LL inf64=inff*(LL)inff; using namespace std; int n,d; int num[3*nn]; int sum[3*nn]; int mxid; int ha[nn*1000]; int hashx(int x,int y) { int ix=x+30007*y; int key=ix%mod; int i; for(i=key;;i=(i+1)%mod) { if(ha[i]==-1) { ha[i]=ix; break; } else if(ha[i]==ix) break; } return i; } int dp[nn*1000]; int dfs(int id,int l) { if(l==1||l==2) return sum[mxid]-sum[id-1]; int ix=hashx(id,l); if(dp[ix]!=-1) return dp[ix]; int re=0; if(id+l<=mxid) re=max(re,dfs(id+l,l)); if(id+l-1<=mxid&&l-1>0) re=max(re,dfs(id+l-1,l-1)); if(id+l+1<=mxid) re=max(re,dfs(id+l+1,l+1)); return dp[ix]=re+num[id]; } int main() { int i,x; while(scanf("%d%d",&n,&d)!=EOF) { memset(num,0,sizeof(num)); memset(ha,-1,sizeof(ha)); memset(dp,-1,sizeof(dp)); sum[0]=0; for(i=1;i<=n;i++) { scanf("%d",&x); num[x]++; mxid=x; } for(i=1;i<=mxid;i++) { sum[i]=sum[i-1]+num[i]; } if(d==1||d==2) printf("%d\n",n); else printf("%d\n",dfs(d,d)); } return 0; }