Codeforces Round #521 (Div. 3) F2 单调队列dp

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ccsu_cat/article/details/84196954

F2. Pictures with Kittens (hard version)

题意:给你n个有权值的点,你从位置0开始,每次操作可以跳到当前位置+k范围内的点并获得其权值(不能跳到原点),要求必须操作x次,且最后的位置+k必须要大于n,求可以获得的最大权值。

思路:设d[ i ][ j ]为经过 j 次操作到达第 i 个位置所获得的最大权值,方程很容易想到 d[ i ][ j ]=max(d[ p ][ j-1 ])+a[ i ],p+k>=i,但是这个方程复杂度是n*x*k,过不了这题,但是我们可以用单调递减队列去维护d[ p ][ j-1 ]的值,复杂度就降低成了n*k。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=5005;
int a[maxn];
ll d[maxn][maxn];
int q[maxn];
int main()
{
	int n,k,x;
	scanf("%d%d%d",&n,&k,&x);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	if(k*x+k-1<n||x>n)
	{
		puts("-1");
		return 0;
	}
	ll ans=0;
	d[0][0]=1;
	for(int j=1;j<=x;j++)
	{
		int front=1,rear=0;
		q[++rear]=j-1;
		for(int i=j;i<=n;i++)
		{
			while(front<=rear&&q[front]+k<i)front++;
			if(front<=rear&&d[q[front]][j-1]!=0)
			d[i][j]=d[q[front]][j-1]+a[i];
			while(front<=rear&&d[q[rear]][j-1]<=d[i][j-1])rear--;
			q[++rear]=i;
			if(j==x&&i+k>n)
			ans=max(ans,d[i][j]);
		}
	}
	cout<<ans-1;
}

猜你喜欢

转载自blog.csdn.net/ccsu_cat/article/details/84196954