【NOIP2017普及组正式赛】跳房子

这一题很有它的价值,我都要认真思考一番才能够想到方法。

先讲一下题目大意

题目描述

       跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。跳房子的游戏规则如下:
      在地面上确定一个起点,然后在起点右侧画 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,真的是不幸中的不幸)

你可能感兴趣的:(NOIP)