题目描述
奶牛们正在回味童年,玩一个类似跳格子的游戏,在这个游戏里,奶牛们在草地上画了一行N个格子,(3 <=N <= 250,000),编号为1..N。就像任何一个好游戏一样,这样的跳格子游戏也有奖励!第i个格子标有一个数字V_i(-2,000,000,000 <=V_i <= 2,000,000,000)表示这个格子的钱。奶牛们想看看最后谁能得到最多的钱。规则很简单: * 每个奶牛从0号格子出发。(0号格子在1号之前,那里没钱) * 她向N号格子进行一系列的跳跃(也可以不跳),每次她跳到的格子最多可以和前一 个落脚的格子差K格(1 <= K <= N)(比方说,当前在1号格,K=2, 可以跳到2号和3号格子) *在任何时候,她都可以选择回头往0号格子跳,直到跳到0号格子。另外,除了以上规则之外,回头跳的时候还有两条规则: *不可以跳到之前停留的格子。 *除了0号格子之外,她在回来的时候,停留的格子必须是恰巧过去的时候停留的某个格子的前一格(当然,也可以跳过某些过去时候停留的格子)。简单点说,如果i号格子是回来 停留的格子,i+1号就必须是过去停留的格子,如果i+1号格子是过去停留的格子,i号格子不一定要是回来停留的格子。(如果这里不太清楚的可以去看英文原文)她得到的钱就是所有停留过的格子中的数字的和,请你求出最多奶牛可以得到的钱数。 在样例中,K=2,一行5个格子。 一个合法的序列Bessie可以选择的是0[0], 1[0], 3[2], 2[1], 0[0]。(括号里的数表示钱数) 这样,可以得到的钱数为0+0+2+1+0 = 3。 如果Bessie选择一个序列开头为0, 1, 2, 3, ...,那么她就没办法跳回去了,因为她没办法再跳到一个之前没跳过的格子。序列0[0], 2[1], 4[-3], 5[4], 3[2], 1[0], 0[0]是最大化钱数的序列之一,最后的钱数为(0+1-3+4+2+0 = 4)。
输入
* 第1行 1: 两个用空格隔开的整数: N 和 K * 第2到N+1行: 第i+1行有一个整数: V_i
输出
* 第一行: 一个单个的整数表示最大的钱数是多少。
样例输入
5 2
0
1
2
-3
4
0
1
2
-3
4
样例输出
4
OUTPUT DETAILS:
还有一种可能的最大化钱数的序列是: 0 2 4 5 3 1 0
OUTPUT DETAILS:
还有一种可能的最大化钱数的序列是: 0 2 4 5 3 1 0
考虑到题目叙述可能不太清楚,在这里大致说一下题目大意:奶牛要向前跳格子并在跳到某个格子后要向回跳最终跳回起点,每个格子有一个价值(有正有负),且向前跳时每次最多向前跳K个。在向回跳时每次同样最多跳k个且每次必须跳到去时跳的某个格子的前一个格子,每次跳的不能是去时的格子,求最大获得价值。
询问最大值,考虑贪心、搜索和dp,显然贪心是不行,数据范围搜索也不可过,所以自然想到dp。因为奶牛一定要回去,所以设的dp方程要保证奶牛能回去。因为去和回来所跳距离限制相同,所以去时从x跳到y,回来时一定能从y-1跳到x-1,再结合跳回来的规则不难想出f[i]表示去时当前跳到i且留下i-1作为回来的路所得到的最大价值。因为价值要尽可能大,所以一来一回自然要把i之前的所有正数都跳到,再处理出s[i]表示前i个数中所有正数的和,val[i]表示i点的价值。于是就得出了dp转移方程:
f[i]=max{f[j]+s[i-2]-s[j]}+val[i-1]+val[i],(i-K<=j<i-1)。因为回来时一定要走i-1,所以先把它加上。但答案可不是max{f[i]},因为对于f[i],我们留下了i-1作为回去时的落脚点,所以我们还可以把[i+1,i-1+K]中所有正数点走完,最后的结果就是max{f[i]+s[i-1+K]-s[i]}。因为f[i]的转移只和i,j有关所以可以斜率优化。
最后附上代码。
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long f[250010];
long long s[250010];
int v[250010];
int n,m;
int q[250010];
int l,r;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
if(v[i]>0)
{
s[i]=s[i-1]+v[i];
}
else
{
s[i]=s[i-1];
}
f[i]=-1ll<<60;
}
f[0]=0;
l=r=1;
for(int i=2;i<=n;i++)
{
while(l<=r&&q[l]<i-m)
{
l++;
}
f[i]=f[q[l]]+s[i-2]+v[i-1]+v[i]-s[q[l]];
while(l<=r&&f[q[r]]-s[q[r]]<f[i-1]-s[i-1])
{
r--;
}
q[++r]=i-1;
}
long long ans=s[m];
for(int i=1;i<=n;i++)
{
if(i+m-1<=n)
{
ans=max(ans,f[i]+s[i+m-1]-s[i]);
}
else
{
ans=max(ans,f[i]+s[n]-s[i]);
}
}
printf("%lld",ans);
}