[NOI2015]Day2-T1_跳石头Pascal题解

[NOI2015]Day2T1跳石头Pascal题解

By:机长大大

题目背景
一年一度的“跳石头”比赛又要开始了!
题目描述
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M块岩石(不能移走起点和终点的岩石)。
输入格式
输入文件名为stone.in
输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L≥1 且 N≥M≥0。
接下来 N行,每行一个整数,第 i 行的整数Di(0< Di< L)表示第 i 块岩石与 起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
输出格式
输出文件名为 stone.out
输出文件只包含一个整数,即最短跳跃距离的最大值。
输入输出样例

Input                                Output
25 5 2                               4
2
11
14
17
21

样例说明
输入输出样例说明:将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。
数据范围
对于 20%的数据,0≤M≤N≤10。
对于50%的数据,0≤M≤N≤100。
对于 100%的数据0≤M≤N≤50,000,1 ≤L≤1,000,000,000。
题目分析
这是一道贪心题目,我们要搞懂题目是让我们求的是最小值最大,我们先将试题进行分析。
我们可以暴力枚举要移走的石头,不用想肯定超时。但是如果对于这道题目能想到枚举就已经快到解题的关键了。
不知道大家小时候有没有玩过一个游戏叫猜数?就是一个人A心里想一个1-100000的数字然后另外一个人B来猜,A可以告诉B他猜的数字和A想的数字相比较是大了还是小了,你自然可以从1开始一个一个猜,但是最坏的情况你是不是要猜N次?但是你要是先猜50000然后猜25000或75000这种二分的方法是不是每一次离目标答案又近了一半,利用这种方式来缩小目标答案的区间最多不会超过log2n次,效率是不是大大提升,我们叫这种方法为二分答案:通过枚举答案的方式和标准答案进行比较直到猜测的答案范围极度接近标准答案为止,但是这种方法是有条件的。
(1)查询范围有边界
二分答案必须是在闭区间上进行,题目保证有唯一的解,可行的解未必最优,所以,我们可以通过枚举所有可行的解来寻找最优的解的方式得到答案。
(2)答案具有单调性
用二分答案求解的决定性条件是答案要具有单调性,举个例子,在该题目中,我们不难发现,去除石子的数量和最短跳跃距离是成正相关的,也就是说,如果Xi不能满足条件,那么,所有比Xi要大的值均不可以满足条件。
题目要求选手在比赛过程中最短跳跃距离尽可能大,也就是最小值尽可能最大,对题目稍加分析不难判断出,这个范围是有边界的,也就是该次比赛起点终点的距离L的长度,答案是具有单调性的,所以这道题目我们采取二分答案的方式来解答。
那么我们该如何实现呢?
根据题目意思我们知道,最短的极限跳跃距离不会比0小,跳跃距离一定是正整数,所以0就是查询答案的上界;最大跳跃距离不会比比赛河道要大,所以L就是查询的下界,从猜数游戏我们可以知道最快的方法就是我们要先猜中间的答案,我们可以通过移动子界左右指针然后取中间值的方法来达到每一次取半查找答案的目的。
根据对试题进行分析,我们不难得出,如果一个答案是非法解,那么比这个答案要大的答案均为非法解,所以我们就可以将又指针赋值这次取的中间值,该答案为合法解,那么我们就可以把左指针赋值为中间值,但是这种方法存在的问题就是最后左右指针可能不会重合,因为Pascal的整除(div)相等的答案之差可能为0或1,所以判断出循环的边界条件为左右指针差值小于1。
知道了循环的边界,下面就是解题的关键了。如何判断该答案是合法解或非法解。我们可以做一个Check过程,在这个过程里我们将要模拟移走石子的过程,Check过程的形参为枚举到的答案,这个过程就是用来验证该答案是否合法并更改左右指针。我们可以将答案代入进去,因为该答案的意义是整个跳跃的最小距离,所以如果存在比这个答案还要小的跳跃距离,那么就移除掉这块非法的石头,不用考虑限制条件,等全部做一遍之后再和M进行比较,如果你在满足该答案的情况下需要移除超过M个石头,该解肯定是不合法的,我们就在这个答案的左边继续寻找,如果需要移除掉少于M个石头,那么这个解不合法,我们就在这个解右边寻找,如果这个解等于M那么说明这个解是合法的,但是不一定是最优的,我们再在这个解右边寻找,在这里存在一个问题就是,这个解合法的情况下,这个解一定要在跳跃过程中出现这个解,不然实际情况没有你所给的答案的跳跃距离,这里只要戳个标记加个特判就可以了。经过模拟和计算,不难看出,经过一系列的指针变换后,Left所对应的解就是这个题目的最优解,而Right恰好等于Left+1或等于Left,因为(Left+Left+1)Div 2=Left就是这个题目的解,而且因为只要合法我们就移动左指针所以我们一定能保证这个答案在所有答案的最右边而这个答案右边一个一定是非法解,所以我们输出这个解
P.S:输出的时候还需要做一个特判就是如果石头为0的情况,那么一定是不进行循环的因为Left=Right=0,所以如果输出Left就输出0了,此时的最小跳跃距离就是L的宽度,直接输出L
下面送上代码:

Program Captain;//By:机长大大
Var a:array[0..50001]of longint;
i,D,M,N,L,R,Now,Mid,Cot:longint;
Begin//a数组一定要开到max+1因为还有终点要判断
    Readln(D,N,M);//按照题目要求读入数据
    L:=0;R:=D;//L为左指针,R为右指针
    a[n+1]:=D;//保存终点石块位置
    For i:=1 to N do readln(a[i]);
    While R-L>1 do begin//当左右指针之差小于1时跳出循环
        Cot:=0;//Cot记录为满足答案需要移走石头数量
        Now:=0;//Now记录前面一个没移走的石头以计算跳跃距离
        Mid:=(L+R)div 2;//当前二分答案--Mid
        For i:=1 to N do begin
            If a[i]-a[Now]<Mid then begin//如果距离比答案小,移走石头
                inc(Cot);//累加移动数
                If Cot>M then begin//如果该解不合法
                    R:=Mid//在左半边二分
                    Break;//小剪枝,如果已经移走超过M个石头,立刻二分下一个解
                End;
            End else Now:=i;//Now保存为当前存在的石头
        End;
        If Cot<=M then L:=Mid;//如果该解合法,在右半边二分
    End;
    If N<>0 then Writeln(L);//特判,如果N=0则直接输出河宽
    else Writeln(D);
End.

谢谢阅读!QQ:1786126188欢迎巨佬们和我讨论NOI

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Capzera

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值