[USACO10OPEN]牛跳房子Cow Hopscotch

题目描述

奶牛们正在回味童年,玩一个类似跳格子的游戏,在这个游戏里,奶

牛们在草地上画了一行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格(2 <= K <= N)(比方说,当前在1号格,K=2, 可以跳到2号和3号格子)

*在任何时候,她都可以选择回头往0号格子跳,直到跳到0号格子。另外,除了以上规则之外,

回头跳的时候还有两条规则:

*不可以跳到之前停留的格子。

*除了0号格子之外,她在回来的时候,停留的格子必须是恰巧过去的时候停留的某个格子的前一格(当然,也可以跳过某些过去…

(必须最后要跳回来)

 

题解:
往回跳是一个挺麻烦的事情。

发现,如果不是一下回到0的话,一定会在某一个位置i的前一个位置作为回来的停留

因为不能记录来的路上哪些点没有走过,

所以我们还可以过去的时候,就把回来的路径上的点贡献统计上。

似乎只能dp了、

由于有负数,所以不是停留的格子越多越好。

为了避免选择i的前一个格子是否要停留,

我们设f[i],表示,从0跳到i,往后再跳一些(距离i不能超过k-1),然后返回到i-1,然后再回到0的最优收益。

就是说,我们钦定i-1是返回的路径上第一个回来的点。

而且,我们每跳k个距离,就必须留下一个点作为回来的点。

那么,f[i]=max(f[j]+s[i-2]-s[j]+v[i]+v[i-1]) (i-k<=j<=i-2)就是从j一步跳到i

s[k]表示,前k个位置,大于0的数的前缀和。因为不超过k距离下,随便收钱必然找正数 。

j的范围很经典了,简单又自然地,单调队列优化一下f就可以

统计答案的时候,并不是mx(f[i])

因为i还可以往后捞点钱,再回到i-1,所以,f[i]再加上s[min(i+k-1,n)]-s[i]

 

然后一个锅就是:

我们的讨论有一个大前提。"如果不是一下回到0的话,一定会在某一个位置i的前一个位置作为回来的停留"

那么如果最优解是跳过去,然后一步回到0,就挂了。

所以,f[0]=s[min(0+k),n]也要取进ans的mx里。

 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=(1LL*1<<62);
const int N=250000+4;
int n,k;
ll f[N];
ll v[N],s[N];
int q[N],l,r;
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%lld",&v[i]);
        s[i]=s[i-1]+(v[i]>0)*v[i];
    }
    l=1,r=0;
    f[1]=v[1];
    q[++r]=0;
    for(int i=2;i<=n;i++){
        while(l<=r&&q[l]<i-k) l++;
        f[i]=f[q[l]]-s[q[l]]+v[i]+v[i-1]+s[i-2];
        while(l<=r&&(f[q[r]]-s[q[r]])<=(f[i-1]-s[i-1])) r--;
        q[++r]=i-1;
    }
    ll ans=0;
    for(int i=1;i<=n;i++) ans=max(ans,f[i]+s[min(n,i+k-1)]-s[i]);
    ans=max(ans,f[0]+s[min(n,k)]-s[0]);
    printf("%lld",ans);
    return 0;
}

总结:

像这种题,确实看起来无从下手,主要是回来的路径怎么考虑上?

但是利用回来的路径必须是i的前面一个位置,所以,可以钦定回来的位置,然后就可以dp了。

注意边界的细节问题。

 

转载于:https://www.cnblogs.com/Miracevin/p/9637951.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值