Codeforces 505C Mr. Kitayuta, the Treasure Hunter

题目链接

题目大意

一个人流落在了群岛上,但是他要捡钻石。
一共有30001个岛子,编号从0到30000。这个人从0出发开始跳,第一次跳长度为 d d ,即从0号岛跳到d号岛。而后的跳跃遵循如下规则。

  • 若上一次跳的长度为 last l a s t ,则这一次可以跳 last1,last,last+1 l a s t − 1 , l a s t , l a s t + 1 三种步长。

跳的长度不能为0。
每跳到一个岛子上,他就会捡这个岛子上的钻石。
求他最多捡多少钻石。

思路

一看就应该是DP。考虑最裸的方程。

f[i][j] f [ i ] [ j ] 表示跳到第 i i 个岛子,上一步步长为j,最多捡多少钻石。

转移显然 f[i][j]f[i+j][j],f[i+j1][j1],f[i+1+1][j+1] f [ i ] [ j ] → f [ i + j ] [ j ] , f [ i + j − 1 ] [ j − 1 ] , f [ i + 1 + 1 ] [ j + 1 ]
面临的问题也很显然:时间空间都是 300002 30000 2 ,GG。
但是可以发现一个性质:

这个人步子长度的变化不会特别大
证明:等差数列求和,若他每次加一,即使是初始步长为1,不到250次也会超过30000

那就可以简化一下状态咯。

f[i][j] f [ i ] [ j ] 表示跳到第 i i 个岛子,上一步的步长与初始步长d的差距为 j j <script type="math/tex" id="MathJax-Element-28">j</script>,最多捡到多少钻石。

转移和原来是一样的,控一下边界,因为会有复数第二维开两倍,结束。

代码
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<cctype>
#define pa pair<int,int>
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back

using namespace std;

inline ll read()
{
    long long f=1,sum=0;
    char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
    while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
const int MAXN=30010;
const int N=300;
int f[MAXN*2][2*N+10];
int cnt[MAXN*2];
int main()
{
    int n,d;
    scanf("%d%d",&n,&d);
    for (int i=1;i<=n;i++)
    {
        int x=read();
        cnt[x]++;
    }
    memset(f,-1,sizeof(f));
    f[d][N]=cnt[d];
    for (int i=d;i<30000;i++)
    {
        for (int j=0;j<=2*N;j++)
        {
            if (f[i][j]==-1) continue;
            for (int x=-1;x<=1;x++)
            {
                int len=j-N+d+x;
                if (len<1 || i+len>MAXN || !(j+x)) continue;
                f[i+len][j+x]=max(f[i+len][j+x],f[i][j]+cnt[i+len]);
            }
        }
    }
    int ans=0;
    for (int i=d;i<=30000;i++)
        for (int j=0;j<2*N;j++)
            ans=max(ans,f[i][j]);
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值