洛谷 P3957 跳房子

洛谷 P3957 跳房子

Description

  • 跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。

    跳房子的游戏规则如下:

    在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个 格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:

    玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。

    现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d 。小 R 希望改进他的机器人,如果他花 g 个金币改进他的机器人,那么他的机器人灵活性就能增加 g ,但是需要注意的是,每 次弹跳的距离至少为 111 。具体而言,当 g<d 时,他的机器人每次可以选择向右弹跳的距离为 d−g,d−g+1,d−g+2,…, d+g−2, d+g−1 , d+g ;否则(当 g≥d时),他的机器人每次可以选择向右弹跳的距离为 1 , 2, 3 ,…, d+g−2 , d+g−1 , d+g。

    现在小 R 希望获得至少 k 分,请问他至少要花多少金币来改造他的机器人。

Input

  • 第一行三个正整数 n , d , k ,分别表示格子的数目,改进前机器人弹跳的固定距离,以及希望至少获得的分数。相邻两个数 之间用一个空格隔开。

    接下来 n 行,每行两个正整数 xi,si,分别表示起点到第 i 个格子的距离以及第 i 个格子的分数。两个数之间用一个空格隔开。保证 xi按递增顺序输入。

Output

  • 共一行,一个整数,表示至少要花多少金币来改造他的机器人。若无论如何他都无法获得至少 k 分,输出 −1。

Sample Input

7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2

Sample Output

2

题解:

  • 单调队列 + 二分。
  • 显然这题是一个dp。但是n的范围太大,肯定要用单调队列来优化。
  • 然后对于变量g,很容易想到二分枚举。
  • 思路就是这样,一眼秒。
  • 然后我交了5次艹
  • 难度在于此题单调队列不像以往的题目是一格一格的挪动,而是一段一段的挪动。如果一格一格的挪动数组开不下,时间也过不去。
  • 所以每次都尝试添加一个格子(而不是一个位置)。
  • 框定区间的左端点一直往右走直到走到一个格子的位置 >= 当前位置 - g - d。
  • 框定区间的右端点一直往右走直到走到一个格子的位置 <= min(当前位置 - 1, 当前位置 - d + g)
  • 那么走完之后框定区间内的格子就是可以转移到当前格子的格子,进行转移即可。
  • 注:往右走一下是走到下一个格子的位置(而不是走到下一个位置)
#include <iostream>
#include <cstdio>
#include <deque>
#define int long long
#define N 500005
#define inf 100000000000ll
using namespace std;

struct Node {int val, pos;};
struct A {int val, pos;} a[N];
int n, d, k, sum;
int dp[N];
deque<Node> deq;

int read()
{
    int x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
    return x *= f;
}

bool check(int g)
{
    int dfn = 0;
    deq.clear();
    for(int i = 1; i <= n; i++)
    {
        int pos = a[i].pos;
        while(deq.size() && deq.front().pos < pos - g - d) deq.pop_front();
        while(1)
        {
            if(a[dfn].pos < pos - g - d) dfn++;
            else if(a[dfn].pos >= pos - g - d && a[dfn].pos <= min(pos - 1, pos - d + g))
            {
                int val = dp[dfn];
                while(deq.size() && deq.back().val <= val) deq.pop_back();
                deq.push_back((Node){val, a[dfn++].pos});
            }
            else break;
        }
        if(!deq.size()) dp[i] = -inf;
        else dp[i] = deq.front().val + a[i].val;
        if(dp[i] >= k) return 1;
    }
    return 0;
}

signed main()
{
    cin >> n >> d >> k;
    for(int i = 1; i <= n; i++)
    {
        a[i].pos = read();
        a[i].val = read();
        if(a[i].val > 0) sum += a[i].val;
    }
    if(sum < k) {cout << -1; return 0;}
    int l = 0, r = inf, ans;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(check(mid)) r = mid - 1, ans = mid;
        else l = mid + 1;
    }
    cout << ans;
    return 0;
}

转载于:https://www.cnblogs.com/BigYellowDog/p/11518797.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值