二分最短跳跃距离,用贪心扫描统计必须移走的石头数。
OJ: noi_openjudge
题目 ID: ch0111-10
难度:普及+/提高
标签:二分贪心python
日期: 2026-07-30 23:01
题意
最多移走
思路
二分候选最短距离 distance。从左到右扫描,当前石头与上一个保留位置的距离不足时必须删除一个,贪心地计数即可得到满足该距离至少需要删多少块。若不超过
代码
Python代码
python
river_length, rock_count, removal_limit = map(int, input().split())
rocks = [0] + [int(input()) for _ in range(rock_count)] + [river_length]
def can_keep_minimum_distance(distance: int) -> bool:
removed = 0
previous = 0
for position in rocks[1:]:
if position - previous < distance:
removed += 1
else:
previous = position
# 最后一次不足时,计数等价于删去最后保留的石头;后面已无石头,不影响计数。
return removed <= removal_limit
low, high = 1, river_length
while low < high:
middle = (low + high + 1) // 2
if can_keep_minimum_distance(middle):
low = middle
else:
high = middle - 1
print(low)C++代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
int L;
int n;
int m;
bool check(long long min_jump){
int pre = 0; //上一个没有删除的石头的下标
int cnt = 0;
for(int i =1;i<=n;i++) {
if( a[i] - a[pre] < min_jump)
{
cnt++;
if( cnt > m) return 0; //超过了删除的石头的个数
}
else {
pre = i; //当前作为不删除的上一个石头的下标
}
}
//特判,
if( a[n+1] - a[pre] < min_jump) {
cnt++;
}
if (cnt > m)
return 0;
return 1;
}
long long bs_find(long long l,long long r) {
while( l < r) {
long long mid = (l+r) >>1;
if( !check(mid) )
r = mid;
else
l = mid+1;
}
return l;
}
void init() {
cin >> L >> n >> m;
a[0] = 0;
for(int i =1;i<=n;i++)
cin >> a[i];
a[n+1] = L;
}
int main() {
init();
int ans = bs_find(1,1e9+5);
cout << ans - 1;
// for(int i=1;i<=11;i++) {
// cout << i << " " << check(i) << endl;
// }
return 0;
}复杂度
时间复杂度为
总结
最大化最小跳跃距离是典型的二分答案问题,判定过程由局部贪心完成。