2019.8.11校内赛G题NKOJ4712CF505C收集宝石

探讨一款收集宝石的手游中,如何通过最优飞行路径策略,使游戏角色小飞侠在有限的飞行条件下,收集到最多的宝石。分析了游戏规则,提出了动态规划算法解决最大宝石收集数的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

何老板最近在玩一款收集宝石的手游。游戏虽然简单,但他仍然乐此不疲。
游戏中,有300013000130001个小岛排成一条直线,小岛从左往右编号000300003000030000,相邻岛屿间距为111
nnn块宝石分布在这些岛上,其中第iii块宝石分布在PiPiPi号岛上。
游戏规则如下:
游戏开始时,游戏角色小飞侠位于000号小岛。何老板操控小飞侠往右飞行。
111次飞行距离只能是ddd米。也就是第一次飞行只能飞到d号小岛去着陆。
i(i>1)i(i>1)i(i>1)次飞行的距离可从Len−1,Len,Len+1Len-1, Len, Len+1Len1,Len,Len+1中任选一个。LenLenLen是第i−1i-1i1次飞行的距离。
如果没法继续往右飞行了,游戏结束。
每着陆到一个岛上,小飞侠就可以收集该岛上的所有宝石。何老板想知道,他最多能获得多少块宝石。

输入格式:

第一行,两个整数nnnddd,
1 ≤ n, d ≤ 300001 ≤ n, d ≤ 300001n,d30000
接下来nnn行,每行一个整数,其中第i行表示第i块宝石所在的小岛的编号PiPiPi
1 ≤ i ≤ n1 ≤ i ≤ n1in
d ≤ P1 ≤ P2 ≤ ... ≤ Pn ≤ 30000d ≤ P1 ≤ P2 ≤ ... ≤ Pn ≤ 30000dP1P2...Pn30000

输出格式:

一个整数,表示何老板最多能够收集到的宝石数量

解:

我们对于这道题,有一个十分显然的方式:
f[i][j]f[i][j]f[i][j]表示从iii号岛出发,出发的速度为jjj
很显然有一个方程:
f[i][j]=max⁡{f[i+j][j]i+j&lt;= 30000f[i+j+1]j+1]i+j+1&lt;= 30000f[i+j−1][j−1]j&gt;1f[i][j]=\max\left\{ \begin{array}{ll} f[i+j][j] &amp; \textrm{i+j&lt;= 30000} \\ f[i+j+1]j+1] &amp;\textrm{i+j+1&lt;= 30000}\\ f[i+j-1][j-1] &amp;\textrm{j&gt;1} \end{array}\right.f[i][j]=maxf[i+j][j]f[i+j+1]j+1]f[i+j1][j1]i+j<=30000i+j+1<=30000j>1
但是显然我们发现这样时间复杂度是O(nd)O(nd)O(nd)
 300002 30000^2300002的数字我们显然放弃。
但是我们突然发现。
为什么题目说的是300013000130001?为什么要固定?
我们来考虑下ddd的偏移量。
我们惊奇地发现:
∑1245&gt;30001\sum_{1}^{245}&gt;300011245>30001
考虑了一下,所以说在实际决策中我们用到的速度其实只有500500500种(约等于490490490
这就把第二维降到了500500500的程度

#include<bits/stdc++.h>
using namespace std;
template<typename T>
void rin(T &t)
{t=0;int k=1;char c=getchar();while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}while(isdigit(c)){t=t*10+c-'0';c=getchar();}t*=k;}
typedef long long ll;
const int maxn=30001+5;
int pos[maxn],a[maxn],n,d,ans,f[maxn][505];
int dp(int x,int dv)
{
    int v=dv-250+d;
    if(x>pos[n])return 0;
    if(f[x][dv]!=-1)return f[x][dv];
    f[x][dv]=0;
    f[x][dv]=max(f[x][dv],dp(x+v+1,dv+1));
    f[x][dv]=max(f[x][dv],dp(x+v,dv));
    if(v>1)f[x][dv]=max(f[x][dv],dp(x+v-1,dv-1));
    f[x][dv]=f[x][dv]+a[x];
    return f[x][dv]; 
}
int main()
{
    rin(n);rin(d);
    memset(f,-1,sizeof(f));
    for(int i=1;i<=n;i++){rin(pos[i]);a[pos[i]]++;}
    cout<<dp(d,250);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值