简要题意:
你前面一共有 n n n 个格子,每个格子都有它的分值 a x a_x ax。当你到达第 x x x 个格子就能获得第 x x x 个格子的得分 a x a_x ax。
初始时你站在第 1 1 1 个格子,每一次移动你可以选择向左或向右,特别地,向左移动的次数不能超过 z z z。
现在,请问你正好走了 k k k 步后,最大得分是多少?
这道题比赛时,我先写了个正解,结果发现出了点问题,然后不知道怎么想的,去写了 2 2 2 个显然是假的的贪心,然后又跑回去重新按照第 1 1 1 次的思路写了一遍,然后就过了。于是我就丢人地做了 50 50 50 分钟 B B B,导致没来得及写完 D D D。
这道题考虑这样一件事,我们没走一步,虽然位置并不一定单调不降,但是向左走的步数和总共走的步数一定是单调不降的,换句话说就是这 2 2 2 个量无后效性,这就是本题的突破口。
我们知道向左走的步数和总共走的步数就可以知道当前在哪个格子,然后我们再来决定这步是从哪里转移过来的,上一步是向右走的还是向左走的。
思路大概就是这样,希望一些细节大家还是自己推一下,还是比较复杂的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
const int MAXN=1e5+10;
int a[MAXN],f[6][MAXN];
int work(){
int n,k,z;
read(n);read(k);read(z);
for(int i=1;i<=n;i++)read(a[i]);
f[0][0]=a[1];
for(int i=0;i<=z;i++)
for(int j=1;j<=k;j++){
f[i][j]=f[i][j-1]+a[j-i*2+1];
if(i!=0)f[i][j]=max(f[i][j],f[i-1][j-1]+a[j-i*2+1]);
}
int ans=0;
for(int i=0;i<=z;i++)ans=max(ans,f[i][k]);
cout<<ans<<endl;
return 0;
}
int main(){
int T;read(T);
while(T--)work();
return 0;
}