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

来源:互联网 发布:关于大数据的新闻报道 编辑:程序博客网 时间:2024/05/18 02:32
这一题很有它的价值,我都要认真思考一番才能够想到方法。

先讲一下题目大意

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