• 【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]]<f[j]) and (head<=tail) do
                                            dec(tail);//删尾
                                    inc(tail);
                                    q[tail]:=j;
                                    last:=j;
                            end;
                    end;
                    while ((a[q[head],1]+zd<a[i,1]) 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]]<f[j]) and (head<=tail) do
                                            dec(tail);
                                    inc(tail);
                                    q[tail]:=j;
                                    last:=j;
                            end;
                    end;
                    while ((a[q[head],1]+zd<a[i,1]) 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,真的是不幸中的不幸)

    转载需注明出处。
  • 相关阅读:
    动画Animation
    ListView配合BaseAdapter
    ListView 搭配SimpleAdapter
    内部类以及内部接口的实例化
    SAP FI模块常用事务代码
    java随机生成字符串和校验
    JAVA版exe可执行加密软件
    微信小程序实现类似JQuery siblings()的方法
    微信小程序实现给循环列表点击添加类(单项和多项)
    微信小程序如何动态增删class类名
  • 原文地址:https://www.cnblogs.com/jz929/p/11817901.html
Copyright © 2020-2023  润新知