[刷题笔记] Luogu P1725 琪露诺

发布时间 2023-08-11 23:18:24作者: SXqwq

Problem

Description

若当前在\(pos\)位置,每次可以在\([pos+l,pos+r]\)区间内任选一个点跳。每跳到一个地方就可以获得这个地方的值,最后跳到位置\(pos \geq n\)即为结束,求如何跳才能使结束的时候获得的值最大?

Analysis

我们发现所有操作都是在一条链上完成的,显然若再次跳到这个位置时的值不如上次则此时继续跳对答案是没有贡献的。也就是说局部最优解可以被利用。同时我们发现前面如何跳到了这个位置对于后面跳是没有影响的,符合无后效性,考虑dp。

我们发现每跳到一个点\(pos\),她对什么有贡献呢?显然是对\(\forall i\in [pos+l,pos+r]\)有贡献。具体地,若定义\(f_i\)表示前\(i\)个点的最大值,则满足:

\(f_i=max(f_i,f_{pos}+a_i)\)

补充:和上文同理满足\(\forall i\in [pos+l,pos+r]\)\(a_i\)指当前位置的值。

Explanation:每个点\(i\)都可以先跳到\(pos\)再跳到\(i\)(因为满足\(\forall i\in [pos+l,pos+r]\),显然一个点\(i\)可能会被访问很多次,因为很可能有很多个\(pos\)可以到\(i\),取max即可)

由于本题数据很水朴素dp就能过,先不写优化了awa

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 200010;
int n,l,r;
int f[2*N];
int a[2*N];
int ans = -1e9;
int main()
{
    scanf("%d%d%d",&n,&l,&r);
    for(int i=0;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    memset(f,-0x3f,sizeof(f)); //由于有负数所以需要初始化为MINN
    f[0] = 0; //依据题意第0为的值为0
    for(int i=0;i<=n;i++) //枚举pos
    {
        for(int j=i+l;j<=i+r;j++) //每个pos对谁产生贡献? 
        {
            f[j] = max(f[j],f[i]+a[j]);
        }
    }
    for(int i=n;i<=n+r;i++) ans = max(ans,f[i]); //由于跳到n外也算离开,观察上面dp程序显然发现最多跳到n+r,取max即可
    cout<<ans<<endl;
    return 0;
}