【洛谷P3957】跳房子

题目大意:给定一个数轴和 N 个点,点有点权,现从 0 位置出发,初始时每次只能走 d 的距离,可以在数轴上任意位置停下,此时,会得到一个点权和。现允许支付 x 的费用,使得每次可以走的距离为一个范围 [max(1,d-x), d+x]。求最少支付多少费用才能使得经过的点权和至少为 k。

题解:根据本题的数据范围可知,需要一个一维的状态和 O(1) 时间内的状态转移。
发现若支付 X 的代价可以满足点权和至少为 K,那么支付更多的代价一定可以满足条件。因此,考虑二分答案,对于每次二分的代价,进行 dp,根据最优解进行判断,是否存在经过一个点时的点权和满足条件。
在 dp 转移时,注意到是连续区间最值的转移方式,因此考虑单调队列。首先,判断有哪些新的状态可以转移到当前状态,将新的状态入队;其次,判断哪些在队内的状态不合法,将其弹出队列;最后进行合法的转移即可。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
typedef long long LL;

LL f[maxn];
int n,d,k,pos[maxn],s[maxn];

bool check(int g){
    static int q[maxn];
    int l=1,r=0,now=0;
    for(int i=1;i<=n;i++)f[i]=-1e15;
    for(int i=1;i<=n;i++){
        while(now<i&&pos[i]-pos[now]>=d-g){
            while(l<=r&&f[now]>=f[q[r]])--r;
            q[++r]=now++;
        }
        while(l<=r&&pos[i]-pos[q[l]]>d+g)++l;
        if(l<=r)f[i]=f[q[l]]+s[i];
    }
    for(int i=1;i<=n;i++)if(f[i]>=k)return 1;
    return 0;
}

int main(){
    scanf("%d%d%d",&n,&d,&k);
    LL sum=0;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&pos[i],&s[i]);
        if(s[i]>=0)sum+=s[i];
    }
    if(sum<k)return puts("-1"),0;
    
    int l=0,r=1e9;
    while(l<r){
        int mid=l+r>>1;
        if(check(mid))r=mid;
        else l=mid+1;
    }
    printf("%d\n",l);
    
    return 0;
} 

转载于:https://www.cnblogs.com/wzj-xhjbk/p/11318139.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值