问题描述:在一条直线上有 n 个点(位置 x[i]),选择其中 k 个点作为奶牛的牛舍,使得任意两个牛舍之间的最小距离尽可能大。求这个最大化的最小距离。
分析:直接安排位置很难。但我们可以判定:最小距离为 mid 是否可行?贪心策略:将点排序后,从第一个点开始,依次选取距离上一个选中的点 ≥ mid 的点,直到选满 k 个或遍历结束。若最后能选到 ≥ k 个,则 mid 可行。显然,mid 越大越难满足,可行性单调递减。因此二分答案。
C++ 实现:
cpp
#include<bits/stdc++.h>usingnamespace std;constint N =1e5+5;int n, k;int pos[N];boolcheck(int mid){int cnt =1;int last = pos[0];for(int i =1; i < n;++i){if(pos[i]- last >= mid){ cnt++; last = pos[i];if(cnt >= k)returntrue;}}return cnt >= k;}intmain(){ cin >> n >> k;for(int i =0; i < n;++i) cin >> pos[i];sort(pos, pos + n);int l =1, r = pos[n-1]- pos[0], ans =0;while(l <= r){int mid = l +(r - l)/2;if(check(mid)){ ans = mid; l = mid +1;// 尝试更大距离}else{ r = mid -1;}} cout << ans << endl;return0;}
死循环:当 l = mid 且 mid = (l+r)/2 向下取整时,若 l = r-1 且 check(l) 为真,mid = l, l = mid 导致区间不变。解决办法:使用 mid = (l+r+1)/2 向上取整,或者采用 l = mid+1 和 r = mid-1 的闭区间写法。
答案不在范围内:初始的 l 和 r 必须保证答案在区间内。例如木材切割最小长度至少为 1,最大为 max(L)。如果可能无解,需单独判断。
整数溢出:mid = l + (r - l) / 2 优于 (l+r)/2 防止大整数加和溢出。
推荐通用模板:
cpp
int l = min_possible, r = max_possible;int ans =-1;while(l <= r){int mid = l +(r - l)/2;if(check(mid)){ ans = mid; l = mid +1;// 向右搜}else{ r = mid -1;}}// ans 即最优解(若存在)