题解 CF1389B 【Array Walk】

简要题意:

你前面一共有 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 
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;
}

你可能感兴趣的:(dp)