这一题很有它的价值,我都要认真思考一番才能够想到方法。
先讲一下题目大意
题目描述
跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:玩家每次都必须跳到当前位置右侧的一个格子内 。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。
现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d。小 R 希望改进他的机器人,如果他花 g 个金币改进他的机器人,那么他的机器人灵活性就能增加 g,但是需要注意的是,每次弹跳的距离至少为 1。具体而言,当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 分,请问他至少要花多少金币来改造他的机器人。
输入
第一行三个正整数 n,d,k,分别表示格子的数目,改进前机器人弹跳的固定距离,以及希望至少获得的分数。 相邻两个数之间用一个空格隔开。
接下来 n 行,每行两个正整数�� �� , �� �� ,分别表示起点到第i个格子的距离以及第i个格子的分数。 两个数之间用一个空格隔开。 保证�� �� 按递增顺序输入。
输出
共一行,一个整数,表示至少要花多少金币来改造他的机器人。若无论如何他都无法获得至少 k 分,输出-1。
样例输入
【输入样例 1】
7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
【输入输出样例 2】
7 4 20
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
样例输出
【输出样例 1】
2
【输入输出样例 1 说明】
花费 2 个金币改进后,小 R 的机器人依次选择的向右弹跳的距离分别为2,3,5,3,4,3,先后到达的位置分别为 2,5,10,13,17,20,对应 1, 2, 3, 5, 6, 7 这 6 个格子。这些格子中的数字之和 15 即为小 R 获得的分数。
【输入输出样例 2】
-1
【输入输出样例 2 说明】
由于样例中 7 个格子组合的最大可能数字之和只有 18 ,无论如何都无法获得 20 分。
数据范围限制
本题共 10 组测试数据,每组数据 10 分。对于全部的数据满足1 ≤ n ≤ 500000,1 ≤ d ≤2000,1 ≤ xi,k≤ 10^9 , |s i | < 10^ 5 。
对于第1,2 组测试数据,n ≤ 10;
对于第3,4,5组测试数据, n ≤ 500
对于第 6,7,8 组测试数据,d = 1
这一题有一点难了,不过,这毕竟是NOIP普及组第四题嘛!
正解——二分+单调队列。
首先二分答案,然后就可以用一个单调队列来做(记得要维持好单调队列)
时间复杂度就不用说了,相信读者们都知道(此题可过)。
上一下主要代码
二分答案部分:
l:=0;//head
r:=a[n,1]+d;//tail
ans:=-1;//输出的最小价值
while l<=r do
begin
mid:=(l+r) div 2;//二分变量
if pd(max(1,d-mid),mid+d) then//pd是用来判断是否成立的,在下面的单调队列有讲
begin
ans:=mid;
r:=mid-1;
end else l:=mid+1;
end;
writeln(ans);
单调队列(判断能否得到k价值)部分:
function pd(zx,zd:longint):boolean;
var
f,q:array[0..500000] of int64;
i,j,head,tail,last:longint;
begin
head:=1;
tail:=1;
last:=-1;//用来优化时间的
q[1]:=0;
f[0]:=0;
for i:=1 to n do
begin
f[i]:=0;
for j:=last+1 to i-1 do
begin
if (a[j,1]+zx<=a[i,1]) and (a[j,1]+zd>=a[i,1]) then//判断能否从a[j,1]跳到a[i,1]
begin
while (f[q[tail]]and (head<=tail) do
dec(tail);//删尾
inc(tail);
q[tail]:=j;
last:=j;
end;
end;
while ((a[q[head],1]+zd1]) or (a[q[head],1]+zx>a[i,1])) and (head<=tail) do
inc(head);//移头
if (head>tail) then f[i]:=-maxlongint
else f[i]:=f[q[head]]+a[i,2];//找到从零点到a[i,1]的最大获得价值
if f[i]>=k then exit(true);
end;
exit(false);
end;
还是上一下源代码吧!
var
a:array[0..500000,1..2] of longint;
n,d,k,i,l,r,ans,mid:longint;
function max(x,y:longint):longint;
begin
if x>y then exit(x)
else exit(y);
end;
function pd(zx,zd:longint):boolean;
var
f,q:array[0..500000] of int64;
i,j,head,tail,last:longint;
begin
head:=1;
tail:=1;
last:=-1;
q[1]:=0;
f[0]:=0;
for i:=1 to n do
begin
f[i]:=0;
for j:=last+1 to i-1 do
begin
if (a[j,1]+zx<=a[i,1]) and (a[j,1]+zd>=a[i,1]) then
begin
while (f[q[tail]]and (head<=tail) do
dec(tail);
inc(tail);
q[tail]:=j;
last:=j;
end;
end;
while ((a[q[head],1]+zd1]) or (a[q[head],1]+zx>a[i,1])) and (head<=tail) do
inc(head);
if (head>tail) then f[i]:=-maxlongint
else f[i]:=f[q[head]]+a[i,2];
if f[i]>=k then exit(true);
end;
exit(false);
end;
begin
assign(input,'jump.in');reset(input);
assign(output,'jump.out');rewrite(output);
readln(n,d,k);
for i:=1 to n do
read(a[i,1],a[i,2]);
l:=0;
r:=a[n,1]+d;
ans:=-1;
while l<=r do
begin
mid:=(l+r) div 2;
if pd(max(1,d-mid),mid+d) then
begin
ans:=mid;
r:=mid-1;
end else l:=mid+1;
end;
writeln(ans);
close(input);
close(output);
end.
这些地方都很容易理解,就是如何实现,因为那些细节真的是太多了!
(今年NOIP普及组没能AK,真的是不幸中的不幸)