洛谷 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;
}