c++题目背景 一年一度的“跳石头”比赛又要开始了! 题目描述 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。 为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。 输入格式 第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证 L≥1 且 N≥M≥0。 接下来 N 行,每行一个整数,第 i 行的整数 D i (0<D i <L), 表示第 i 块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。 输出格式 一个整数,即最短跳跃距离的最大值。 输入样例 25 5 2 2 11 14 17 21 输出样例 4 样例说明 将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4 (从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。 数据范围 对于 20% 的数据,0≤M≤N≤10。 对于 50% 的数据,0≤M≤N≤100。 对于 100% 的数据,0≤M≤N≤5×10 4 ,1≤L≤10 9 。
时间: 2025-08-25 22:21:41 AIGC 浏览: 19
<think>嗯,我现在得解决这个跳石头的问题。题目大意是说,在起点和终点之间有一些岩石,可以移走最多M块,然后让选手在比赛中的最短跳跃距离尽可能大。我们需要找到这个最大的可能的最短距离。
首先,我得理解题目。最短跳跃距离的最大值,也就是说,在移走M块石头之后,所有相邻石头之间的距离中最小的那个,要尽可能的大。那这个问题应该可以通过二分法来求解。因为可能的答案是一个范围,我们需要找到最大的符合条件的那个值。
那如何用二分法呢?可能的思路是,确定一个中间值mid,然后判断是否可以在移走不超过M块石头的情况下,使得所有相邻石头之间的跳跃距离都不小于mid。如果可以的话,说明可能还有更大的值,我们就往更大的方向搜索;如果不行,就往更小的方向调整。
那具体来说,判断的条件函数应该怎么写呢?假设当前判断的mid是候选的最短距离,我们需要遍历所有石头,统计需要移走多少块石头才能保证相邻的石头间距都不小于mid。比如,遍历的时候,记录上一块未被移走的石头的位置,如果当前石头和上一块的距离小于mid,那么这块石头必须被移走,否则就保留,并更新上一块的位置。最后统计移走的石头数量是否小于等于M。
举个例子,比如输入样例中的情况。初始石头的位置是2、11、14、17、21,L=25。移走2块,比如2和14之后,剩下的石头是11、17、21。那么跳跃距离是11-0(起点)=11,17-11=6,21-17=4,25-21=4。所以最短的是4。但根据样例说明,移走的是2和14,那么剩下的石头是11、17、21,起点到11是11,11到17是6,17到21是4,21到终点是4。所以最短是4,这时候需要移走两块,符合条件。
那如何用二分法找到这个最大的最短距离呢?初始的左边界应该是可能的最小值,比如1,右边界是L(例如,当所有石头都被移走时,最短距离就是L)。然后每次取中间值mid,用上述的判断函数计算需要的移走数量是否<=M。如果是,说明可以尝试更大的值,左边界右移;否则,需要减小mid,右边界左移。
那现在的问题是如何高效实现这个判断函数。因为N可能达到5e4,所以这个判断函数的时间复杂度必须是O(N)的,否则总的时间复杂度会很高。而二分的次数大概是log2(L),也就是最多30次左右。所以总的时间复杂度是O(Nlog L),应该是可以接受的。
现在具体步骤:
1. 将输入的岩石位置排序?题目里已经说明输入是按距离递增顺序给出的,所以不需要排序。
2. 预处理岩石数组,可以加上起点0和终点L吗?比如,将数组变成0,然后是输入的各个D_i,最后是L。这样处理之后,计算相邻距离的时候会更方便。
例如,样例中的输入岩石是5块,位置是2、11、14、17、21。加上起点0和终点25,数组变成[0,2,11,14,17,21,25]。这时候总共有N+2块石头。但题目中的N是起点和终点之间的岩石数,不包括起点和终点。所以原来的N是5,输入的数组是这5块的位置。在预处理时,我们可能需要将数组前面加上0,后面加上L吗?或者不需要?
或者,在计算的时候,将起点视为0,终点视为L,然后输入的岩石是在中间的N块。例如,在遍历的时候,起点是0,终点是L。所以初始的prev_pos是0,然后遍历所有的岩石位置,最后到终点L。
所以,正确的处理方式应该是:将输入的岩石数组排序(虽然题目已经给出是递增的),然后插入起点0和终点L吗?或者,起点和终点不作为岩石的一部分,但处理的时候需要考虑这两个点。
比如,在判断函数中,prev初始为0,然后遍历所有的岩石位置,最后处理终点L。例如:prev = 0count =0
for iin 0..n-1:current = rocks[i]
ifcurrent - prev< mid:
count+=1else:
prev= current
最后,还要处理终点:如果 L -prev < mid的话,是否也需要移走?但终点是不能被移走的。所以,在这种情况下,是否应该认为这个mid不合法?
例如,假设prev是最后一个未被移走的石头的位置,然后到终点的距离是否>=mid?如果不满足,那么只能移走前面的石头,但终点不能动。所以,在判断函数中,必须保证最后一段的距离也>=mid。否则,说明无法满足条件,此时需要增加移走的次数,但可能无法做到。所以在判断函数中,应该如何处理终点?
比如,假设我们遍历所有的岩石,然后加上终点。比如,岩石数组是原输入的数组,然后我们添加终点L到数组的末尾?或者,在判断的时候,遍历完所有原岩石之后,还要检查最后一块保留的岩石到终点的距离是否>=mid。如果不满足的话,那么需要额外的处理吗?
比如,假设原岩石的位置处理后,剩下的岩石是保留的。例如:
prev =0
count= 0遍历所有岩石的位置:
如果当前岩石位置 - prev< mid →移走当前岩石,count +=1否则,prev = 当前岩石的位置
最后,判断终点L- prev是否 >=mid。如果这一步不成立,那么必须移走一些岩石,使得最后一段的距离满足。但此时,已经没有岩石可以移走了,因为终点不能动。所以这时候这个mid是不可能的,必须返回false?或者,在处理的时候,是否需要将终点L视为必须保留的最后一个点?比如,正确的判断条件应该是:
在移走一些岩石后,所有相邻的岩石之间的间距都必须>=mid,并且起点到第一个岩石的距离,以及最后一个岩石到终点的距离也必须>=mid?
比如,样例中的情况,移走2和14后的岩石是11、17、21。那么起点到11是11,11到17是6,17到21是4,21到终点是4。所以最后一个跳跃是4。所以最短的是4。
所以,在判断函数中,必须包含终点。所以,在遍历的时候,应该把终点L作为最后一个点,并检查所有的间距是否>=mid。
那如何处理呢?可能的做法是将岩石数组添加L到末尾,或者在遍历完成后单独处理。
例如,假设岩石数组已经包含了0和L?或者不,因为题目中的输入岩石不包括起点和终点。例如,输入的岩石数目是N,起点和终点之外的其他岩石。所以,正确的岩石数组应该是在输入数组的前面加上0,后面加上L吗?或者不需要,因为原输入的岩石是介于0和L之间的?比如,假设输入的岩石数组是已经按升序排列的,不包括0和L。所以,在预处理时,应该将岩石数组变成 rocks =[0] +input_rocks+ [L]。这样,总共有N+2个点。然后,我们需要在这些点之间移除最多M个点(不包括起点和终点),所以可移除的点是原输入的N个岩石中的最多M个。
或者,起点和终点是不能被移除的。所以在预处理时,将岩石数组加上0和L,这样总共有N+2个点,其中0和L不能被移除。然后问题转化为,在这N+2个点中,移除最多M个点(只能是中间的N个中的M个),使得剩下的点之间的最小间距最大。
这可能更方便处理。所以,预处理步骤应该是将输入的岩石数组前面加0,后面加L,形成一个新的数组。然后,这个数组的长度是N+2。比如,样例中的输入处理后,数组变为[0,2,11,14,17,21,25]。
然后,当进行判断时,我们遍历这个数组,并统计需要移除的岩石数目。但需要注意,起点和终点不能被移除。所以,在遍历的时候,第一个点(0)和最后一个点(L)必须保留。
所以,判断函数的逻辑如下:
初始化prev为第一个元素(即0),移除计数count为0。从第二个元素开始遍历:
当前元素current = rocks[i]
计算current - prev的距离,如果这个距离< mid:需要移除current,count +=1else:保留current,prev更新为current遍历完成后,还需要检查最后的prev到L的距离是否 >=mid吗?或者,因为最后的一个元素已经被处理了?
假设预处理后的数组包含L作为最后一个元素,那么在遍历的时候,最后一个元素是L。当处理到L的时候,prev是前面的某个岩石的位置,比如在样例中,遍历到21的时候,prev是17,然后是21到17=4,假设mid是4,那么没问题。然后遍历到L=25,25-21=4,等于mid,所以保留,count不变。最后整个数组的处理完成。
所以,判断函数的正确逻辑应该是:
将预处理后的数组(包括0和L)进行遍历,统计需要移除的岩石数目,其中不能移除0和L。因此,当遍历到这些点时,只能移除中间的岩石。
例如,在预处理后的数组中,遍历除了0之外的每个点,判断是否需要被移除。如果某个点(比如原输入的岩石或者L)在判断中被需要移除,那么如何处理?因为L不能被移除,所以必须保留。所以,如果prev到L的距离小于mid,那么必须移去中间的某些岩石,但此时无法移走L,所以这种情况应该视为无法满足条件。
或者,预处理后的数组中包含L作为最后一个元素,所以在遍历时,当处理到最后一个元素(L)的时候,是否会被移除?比如,假设prev是倒数第二个元素的位置,到L的距离是否 >= mid?如果是,则保留;否则,必须移除L吗?但L不能被移除,所以此时该mid不可能满足条件,所以需要返回false。
所以在处理预处理后的数组时,必须确保最后一个点L不能被移除,因此在遍历时,当遇到当前点(比如L)时,如果current - prev< mid,那么必须移除current,但这是不允许的,因为current是L。所以这种情况说明当前mid太大,无法满足条件,此时需要返回false。
或者,这可能意味着在遍历过程中,当遇到current是L的时候,必须检查current- prev是否 >=mid。如果小于的话,那无论如何都不能满足条件,所以必须将count设置为超过M的值,使得函数返回false。
所以,正确的判断函数逻辑应该如何处理这种情况?可能的思路是:预处理后的数组是包含0和L的,所以当处理到L的时候,必须保留。所以,在处理数组的时候,循环应该从第二个元素开始,到倒数第二个元素(原输入的岩石)进行处理,而最后一个元素(L)必须保留。或者,需要将L作为必须保留的点,所以在处理到倒数第二个元素之后,单独处理到L的距离。
这似乎有点复杂。可能正确的处理方式是在预处理后的数组中,遍历所有的元素,除了第一个0之外。然后,在处理每个元素的时候,判断是否需要移除,但遇到L的时候必须保留。例如:prev = rocks[0] =0
count =0
for iin 1 tolen(rocks)-1:current = rocks[i]
if current- prev < mid:
count +=1
ifcurrent == L:# 如果current是L,不能被移除,所以这种情况必然失败?# 但此时current是L的话,说明prev到L的距离小于mid。由于L不能被移除,所以无法满足条件。此时应该将count设置为M+1,这样就会返回False
count= M+1breakelse:prev = current最后,在处理完所有中间岩石后,需要检查prev到L的距离是否>= mid。比如,此时prev是最后一个被保留的岩石的位置,而L必须保留。所以,如果L- prev < mid的话,说明这段距离不够,此时需要移走中间的某些岩石,使得这段距离满足。或者,这已经无法改变,所以此时该mid不可行。
那如何处理这种情况呢?
例如,假设在处理完所有中间岩石之后,剩下的prev到L的距离是否>=mid?如果不是的话,无论移走多少中间的岩石,都无法改变这一点,所以此时mid不可行。
所以,在判断函数中,除了统计移走的岩石数目之外,还需要最后检查prev到L的距离是否>=mid。如果这个距离不够的话,那么无论怎么移走中间的岩石,都无法满足条件,所以此时返回False。
那么,判断函数的逻辑应该是:
预处理后的数组包含0和L。
初始化prev=0,count=0.
遍历数组中的元素,从索引1到索引len(rocks)-2(因为最后一个元素是L,不能被移除)?
或者,遍历所有元素,包括L前面的元素?
例如,正确的预处理数组是 [0, ... ,L]。假设我们遍历所有元素,包括中间的岩石和L之前的元素,但在处理时,当处理到L时,必须保留,所以如果prev到L的距离小于mid,那么无法满足条件。
或者,我们需要在遍历的时候,不处理L,而是在最后单独处理。
可能更简单的方式是将预处理后的数组包含所有岩石,包括0和L。然后,遍历从索引1到len(rocks)-1的所有元素,包括L。但此时,在遍历到L的时候,如何处理?
例如:prev = 0count =0for i in1 to len(rocks)-1:
current= rocks[i]if current ==L:
#必须保留,所以检查是否满足条件
ifcurrent - prev< mid:
#此时无法满足,因为不能移除L。所以整个mid不可行。count +=1# 这样会导致count超过M?
#或者,直接返回False?
else:
prev =current
break# 因为后面没有元素了?
else:# 普通岩石的处理if current - prev< mid:count +=1else:prev = current最后,还要检查到L的距离是否>=mid。这可能比较麻烦。
或许更正确的方式是在处理时,将L作为最后一个点,但必须保留。所以在遍历时,处理所有原输入的岩石和中间的岩石,最后处理L?
或者,可以这样处理:prev = 0count =0遍历所有原输入的岩石(不包括L):
current= rocks[i]if current -prev < mid:count +=1else:prev = current处理完原输入的所有岩石之后,检查prev到L的距离是否 >=mid。如果否的话,那么即使移走中间的岩石,也无法满足条件。此时,当前的mid不可行。所以,在这种情况下,应该如何处理?
比如,假设原输入的岩石处理完之后,剩下的prev是某个位置,到L的距离为d。如果d < mid,那么不管移走多少中间岩石,都无法让这段距离>=mid。所以,这样的mid无法满足条件。这时候,我们需要在判断函数中,将这种情况视为不能满足条件,即返回False。
所以,此时判断函数的条件应该是:
如果处理后的岩石之间的间距都>=mid,并且最后一段(prev到L)的距离也>=mid。所以,在判断函数中,除了统计移走的岩石数目之外,还需要检查最后一段的距离是否足够。所以,判断函数的步骤:
1. 预处理岩石数组,添加0和L?
或者,原输入的岩石数组没有0和L,所以在处理的时候,初始的prev是0,然后遍历原输入的所有岩石,处理之后,再处理L的位置。
比如:
prev =0count=0for d ininput_rocks_sorted:if d - prev< mid:count +=1else:prev =d最后,检查L- prev >=mid吗?如果否,那么整个条件不满足。
所以,判断函数返回的条件是:count<=M,并且L - prev >=mid。
这样,这个判断函数是否正确?
举个例子,样例输入:
输入岩石是2,11,14,17,21,L=25,mid=4.
处理过程:
初始prev=0.
第一个岩石是2,0到2的距离是2 <4→ 移走,count=1.
第二个岩石是11:11-0=11 >=4 → 保留,prev=11.
第三个岩石是14:14-11=3<4 → 移走,count=2.
第四个岩石是17:17-11=6>=4 →保留,prev=17.
第五个岩石是21:21-17=4 >=4 →保留,prev=21.
然后处理L=25:25-21=4 >=4→ 满足条件。
此时count=2 <=M=2。所以条件满足,返回True.
所以,此时可以认为mid=4是可行的。这样,判断函数的逻辑是:
计算需要移走的岩石数目count,并且最后一段的距离是否>=mid。如果两个条件都满足,则返回True;否则,返回False。
所以,判断函数的代码可以这样写:
defis_possible(mid, rocks,L, M):prev =0
count= 0for d inrocks:
ifd - prev< mid:
count+=1else:
prev= d# 检查最后一段到终点的距离
ifL - prev< mid:
returnFalse
returncount <= M这样是否正确?
那在样例中,当mid=4时,count=2(移走2和14),并且最后一段是21到25是4,满足条件。所以返回True。是正确的。
那假设另一个情况,比如mid=5:
处理过程:prev=0.岩石2:0到2<5→ count=1.
岩石11:0到11≥5 → prev=11.
岩石14:14-11=3 <5→ count=2.
岩石17:17-11=6≥5 →prev=17.岩石21:21-17=4<5 → count=3.
此时,count=3> M=2,所以返回False。所以mid=5不可行。所以,这说明判断函数是正确的。
那现在,问题转化为,找到最大的mid,使得is_possible(mid)为True。
那现在如何用二分法来找这个mid?
初始的left是1,right是L(比如样例中的25)。然后,每次取mid = (left+ right +1)//2。这里使用左闭右闭区间,所以当mid满足条件时,left=mid,否则 right=mid-1。这样,最终left就是最大的满足条件的mid。
例如:
初始left=1, right=25.
mid=(1+25+1)/2=13.
调用is_possible(13):
处理所有岩石:
prev=0.
岩石2:0到2<13 → count=1.
岩石11:0到11<13 → count=2.
岩石14:0到14=14≥13 → prev=14.
岩石17:17-14=3 <13→ count=3.
岩石21:21-14=7 <13 →count=4.count=4>M=2 →返回False.所以此时right=13-1=12.
接着,mid=(1+12+1)/2=7.判断is_possible(7):
prev=0.
岩石2<7 →count=1.岩石11:0到11≥7→ prev=11.
岩石14-11=3<7 → count=2.
岩石17-11=6<7 → count=3.
岩石21-11=10≥7 →prev=21.最后到L=25,25-21=4<7 → 所以返回False.
所以,此时right=6.
mid=(1+6+1)/2=4.
判断mid=4是否可行,返回True。此时left=4,right=6.
下一步,left=4, right=6 → mid=(4+6+1)/2=5.5→取整为5.
mid=5.
is_possible(5):处理过程:
prev=0.
岩石2 → 0到2 <5→ count=1.
岩石11 →0到11≥5 → prev=11.
岩石14 → 14-11=3<5 → count=2.
岩石17-11=6≥5 →prev=17.岩石21-17=4 <5→ count=3.
此时count=3>M=2 → 返回False.
所以,right=5-1=4.循环结束。最大的可行mid是4.
所以,这样二分是正确的。
综上,整个算法的大致步骤是:预处理输入,将岩石数组排序(虽然题目已经给出排序,但可能不需要?因为题目已经说明输入是按递增顺序给出的)。所以,可以直接读取输入的数组,然后进行排序吗?或者不需要,直接使用输入的数组?题目中的输入说明“这些岩石按与起点距离从小到大的顺序给出”,所以输入的数组已经是升序排列的。所以,不需要再排序。所以,正确的处理步骤是:
读取L, N,M.
读取N个整数,组成数组rocks。然后,对rocks数组进行处理,是否包含0和L?不需要,因为原问题中的起点是0,终点是L,中间的岩石是输入的N个点。所以,在遍历的时候,prev初始化为0,然后处理所有输入的岩石,最后处理L。
所以,判断函数可以如之前所述。
综上,整个算法的代码结构:
读取输入。对输入的数组排序(可能不需要,因为题目给出已经排序)。
用二分法寻找最大的mid,使得is_possible(mid)为True.
其中,is_possible函数实现上述逻辑。
现在,如何用C++实现这个代码?
首先,输入的读取:输入L, N, M.
然后,读取N个整数,存入数组rocks中。然后,排序这个数组(题目中说已经按顺序给出,所以可能不需要?但为了保险起见,可能需要排序?比如,可能输入的测试用例是否严格正确?如果题目保证输入顺序正确的话,可以不用排序。)
例如,样例中的输入是:
255 22
1114
1721
这些已经是按升序排列的。所以,假设输入是正确的,不需要排序。但在代码中,是否需要添加排序?
比如,如果输入的D_i不是按升序给出的,那么处理就会出错。所以,按照题目描述,输入的D_i是按升序给出的。所以,代码中不需要排序。
所以,可以跳过排序步骤。所以,代码的大致结构:
#include<iostream>
#include<vector>
#include<algorithm>
usingnamespace std;
boolis_possible(intmid, const vector<int>& rocks,int L, intM) {int prev =0;
intcount = 0;
for (int d : rocks) {
if(d - prev< mid) {count++;} else {prev = d;}
}// 检查最后一段到L的距离
if(L - prev< mid) {return false;}
returncount <= M;}
int main(){
int L, N, M;
cin >>L >> N >>M;
vector<int> rocks(N);
for (int i =0; i< N; ++i) {
cin>> rocks[i];}
intleft = 1, right = L;
int ans= 0;while (left<= right) {int mid =left + (right- left +1) /2;
if(is_possible(mid, rocks, L, M)) {
ans= mid;left = mid +1;} else {right = mid -1;}
}cout << ans << endl;
return0;
}这样是否正确?
测试样例:
输入:25 52
211
1417
21运行这个程序,mid的计算是否如预期?
当mid=4时,is_possible返回true。而当mid=5时,返回false。所以,输出应该是4,符合样例。但是,在代码中,有一个问题:在判断最后一段到L的距离的时候,如果当前处理后的prev是最后一个岩石的位置,那么L - prev是否>=mid?比如,假设原输入的岩石被处理后,prev是最后一个未被移除的岩石的位置,那么到L的距离是否>=mid?
是的,所以该判断是正确的。
那现在,这样是否覆盖了所有情况?
例如,假设所有岩石都被移走的情况下。比如,M=N,可以移走所有岩石。此时,prev初始为0,所有岩石都被移走,count=N,最后到L的距离是L-0>=mid。此时,是否满足条件?
是的。此时,最短跳跃距离是L,因为只有起点和终点。所以,此时mid的最大可能值是L。
所以,当M=N时,最大可能的mid是L。
所以,这样的代码在这种情况下应该能正确处理。那现在,该代码是否正确?
另一个测试案例,比如L=10,N=0,M=0。此时,没有中间岩石,所以最短跳跃距离是10。此时,is_possible的判断:prev=0,然后遍历空数组。count=0。然后检查L-0 >=mid→ 10 >=mid。所以,当mid=10时,返回true。当mid=11时,返回false。所以,输出是10。正确。
另一个测试案例:L=10,N=1,M=1。中间岩石在位置5。此时,可以移走该岩石。此时,最短跳跃距离是10。判断函数如何处理?当mid=10:
prev=0,处理岩石5。5-0=5 <10→ count=1。count <=M=1。然后检查L - prev=10-0=10 >=10→是的。所以返回true。所以,mid=10是可行的。所以,最大是10。
正确。
那另一个案例:N=1,M=0。岩石位置是5。此时,不能移走岩石。所以,跳跃距离是5和5(5到10)。最短是5。此时,mid的可能最大值是5。代码是否正确?
当mid=5:prev=0,岩石5。5-0=5>=5 →保留,count=0。最后检查10-5=5>=5 →是的。所以返回true。left=5+1=6,尝试mid=6.
mid=6:岩石5-0=5<6 →count=1>M=0.返回false。所以,最终ans=5.
正确。
综上,该代码似乎正确。
但可能有一个问题:在判断函数中,当mid的取值较大时,可能会导致某些情况下,虽然移走的岩石数目<=M,但最后一段到L的距离不足。例如:
假设L=10,中间岩石是3和7。M=1.
假设mid=4.
处理过程:
prev=0.
3-0=3 <4→count=1.
7-0=7 >=4→prev=7.
count=1<=M=1.
最后到L=10的距离是3。3<4 →返回false.
所以,虽然count<=M,但最后一段的距离不够,所以该mid不可行。此时,判断函数正确返回false.所以,代码中的判断函数正确。综上,该代码应该可以正确解决问题。</think>使用C++实现的解决方案:
```cpp
#include <iostream>
#include <vector>
using namespace std;
bool isPossible(int mid,
阅读全文