505C (DP)

//题意:30001个岛屿自西向东依次编号为0-30000,输入n个编号,表示该岛屿上有宝藏。
//从编号为0处出发向东跳,第一次跳的距离为d。设上一次跳的距离为L(L>0),则当前跳的距离为L-1(L>1)或L或L+1
//输出最多能拿的宝藏数目

//以p[i]表示第i个岛屿的宝藏数目,dp[i][j]表示跳了距离j到第i个岛屿所获得的最多宝藏数,则状态转移方程为:
//    dp[i+j-1][j-1]=max(dp[i+j-1][j-1],dp[i][j]+p[i+j-1])  比上一次跳的距离减少1
//    dp[i+j][j]=max(dp[i+j][j],dp[i][j]+p[i+j])            和上一次跳的距离相等
//    dp[i+j+1][j+1]=max(dp[i+j+1][j+1],dp[i][j]+p[i+j+1])  比上一次跳的距离增加1

//注意这里dp[i][j]中,i,j的取值均为1-30000,如果开30000*30000的数组会超出内存限制。

//第一次跳的距离d,设第k次跳的距离为D[k],以下说明|D[k]-d|<250
//要使|D[k]-d|尽可能大,不妨令D[k]=D[k-1]+1,即每次相比上一次跳动距离增加1
//要使|D[k]-d|尽可能大跳的次数也应该越多,第一次跳的距离d也就应该越小,故取d=1。
//则有D[1]+D[2]+D[3]+……+D[k]=d+(d+1)+(d+2)+……+(d+k-1)<30000
//k*d+k*(k-1)/2<30000
//即k+k*(k-1)/2<30000,k*k+k-60000<0,k<250,就是说在保证|D[k]-d|尽可能大的情况下,跳的次数不会超过250次。

//所以第k次跳的距离与第一次跳的距离之差的绝对值不会超过250
//因此,可以以第一次跳的距离d为基准来表示跳动的距离D。设j=D-d+250,则0<j<500
//实际跳动的距离为D=j+d-250

//这样把规模降到了30000*500

#include<bits/stdc++.h>
using namespace std;
int p[30001];
int dp[30001][501];
int main()
{
    int n,d,maxn=0;
    int ans;
    cin>>n>>d;
    int i,j;
    memset(dp,-1,sizeof(dp));
    memset(p,0,sizeof(p));
    for(i=1;i<=n;++i)
    {
        int temp;
        cin>>temp;
        ++p[temp];
        maxn=max(maxn,temp);
    }
    dp[d][250]=ans=p[d];
    for(i=d;i<=maxn;++i)
        for(j=1;j<500;++j)
        {
            if(dp[i][j]==-1) continue;
            int D=i+j+d-250;    //保持上一次跳动距离跳到的岛屿编号
            if(D-1>30000)continue;
            if(D-i>1)
            {
                dp[D-1][j-1]=max(dp[D-1][j-1],dp[i][j]+p[D-1]);
                ans=max(dp[D-1][j-1],ans);
            }
            if(D>30000) continue;
            dp[D][j]=max(dp[D][j],dp[i][j]+p[D]);
            ans=max(ans,dp[D][j]);
            dp[D+1][j+1]=max(dp[D+1][j+1],dp[i][j]+p[D+1]);
            ans=max(ans,dp[D+1][j+1]);
        }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值