LeetCode 1552. 两球之间的磁力
题目描述


题意分析
给定若干不同的整数篮子位置,要从中选择
m个放球,每个选中篮子放一个球。两球之间的磁力定义为位置距离,要求所有球对中最小的距离尽可能大。求这个最大的最小距离,不是让总距离或最远两球距离最大。题目保证
2 <= m <= n,位置互不相同,因此至少可以做到相邻所选距离为一。
解法:二分答案 + 贪心验证
核心思路
[!blue]
先排序位置。对于按位置顺序放好的球,任意非相邻两球的距离都是中间若干相邻间距之和,因此只要相邻已放球的距离都至少为
d,所有球对就都满足要求。固定候选
d后,从最左篮子放第一球,再总是选择距离上一球至少为d的最早位置。这个贪心能放最多球:逐个比较任意合法方案,贪心第一球不会更晚;如果贪心前一球不晚于方案前一球,那么方案的下一位置对贪心也仍满足距离限制,所以贪心找到的最早可用下一位置也不会更晚。归纳下来,贪心不会比任何方案更早耗尽可选空间。因此扫描能放下至少
m个球,就证明距离d可行;放不下,则其他摆法也不可能。只需检查是否达到数量,多余篮子可以不用。距离可行时,更小的距离也可行;不可行时,更大的也不可行。候选范围下界为一,上界为最右与最左篮子之差,二分寻找最后一个可行整数。
使用上中位数:可行时令
left = mid保留它并继续向右,不可行时令right = mid - 1。上中位数保证区间只差一时仍能缩小,不会卡在左端。边界重合时就是最大可行距离。
解题步骤
- 将位置升序排序,初始化距离范围
[1, maxPosition - minPosition]。- 两界不同就取上中位数作为候选距离。
- 贪心判定时先放最左一球,之后只在与上个已放位置相距足够时放新球。
- 数量达到
m即可判为可行;只有实际放球时才更新上个位置。- 可行保留中点作为左界,不可行移低右界,最终返回重合值。
代码实现
class Solution {
public int maxDistance(int[] position, int m) {
Arrays.sort(position);
int left = 1;
int right = position[position.length - 1] - position[0];
while (left < right) {
// 使用上中位数,可行时保留中点也能让区间收敛。
int mid = left + (right - left + 1) / 2;
if (canPlace(position, m, mid)) {
left = mid;
} else {
right = mid - 1;
}
}
return left;
}
private boolean canPlace(int[] position, int balls, int distance) {
int placed = 1;
int last = position[0];
for (int i = 1; i < position.length; i++) {
// 尽早放下下一球,间距恰好满足也可以选择。
if (position[i] - last >= distance) {
placed++;
// 只有真正放球后才更新上一个落点。
last = position[i];
if (placed == balls) {
return true;
}
}
}
return placed >= balls;
}
}
import "sort"
func maxDistance(position []int, m int) int {
sort.Ints(position)
left, right := 1, position[len(position)-1]-position[0]
for left < right {
// 使用上中位数,可行时保留中点也能让区间收敛。
mid := left + (right-left+1)/2
if canPlace(position, m, mid) {
left = mid
} else {
right = mid - 1
}
}
return left
}
func canPlace(position []int, balls int, distance int) bool {
placed := 1
last := position[0]
for i := 1; i < len(position); i++ {
// 尽早放下下一球,间距恰好满足也可以选择。
if position[i]-last >= distance {
placed++
// 只有真正放球后才更新上一个落点。
last = position[i]
if placed == balls {
return true
}
}
}
return placed >= balls
}
复杂度分析
- 时间复杂度:$O(n\log(n+1)+n\log(D+1))$,其中 $D$ 为最大位置差。先排序,每次二分判定再线性扫描位置。
- 空间复杂度:二分和贪心判定额外为 $O(1)$,排序自身工作区另计;当前实现会将输入数组排序。
关键点总结
[!green]
- 排序后相邻间距满足阈值,就能保证所有球对距离满足阈值。
- 每个球尽早放置,不会减少后续球的可选空间,保证判定正确。
- 距离阈值具有单调性,二分的是最后一个可行距离。
- 可行时保留中点,配合上中位数才能确保推进。
易错点总结
[!yellow]
- 距离恰好等于候选阈值也合法,判定应使用大于或等于。
- 没放球也更新
last,会把距离参照点改成无关篮子,漏掉可行摆法。- 使用下中位数却令
left = mid,可能在相邻边界间不再前进。- 不能直接将总跨度除以球数当作答案,因为中间所需篮子位置未必存在。
- 输入顺序不代表空间顺序,必须先排序再进行最早位置贪心。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 774. 最小化去加油站的最大距离 | 困难 | 两题都在一维位置上优化间距,原题增加站点最小化最大间隔,本题选择位置最大化最小间隔。 |
| 1482. 制作 m 束花所需的最少天数 | 中等 | 同样二分候选阈值,再通过贪心扫描判断能否形成足够数量的选择。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!