圆形靶内的最大飞镖数量
难度:
标签:
题目描述
Alice is throwing n
darts on a very large wall. You are given an array darts
where darts[i] = [xi, yi]
is the position of the ith
dart that Alice threw on the wall.
Bob knows the positions of the n
darts on the wall. He wants to place a dartboard of radius r
on the wall so that the maximum number of darts that Alice throws lie on the dartboard.
Given the integer r
, return the maximum number of darts that can lie on the dartboard.
Example 1:

Input: darts = [[-2,0],[2,0],[0,2],[0,-2]], r = 2 Output: 4 Explanation: Circle dartboard with center in (0,0) and radius = 2 contain all points.
Example 2:

Input: darts = [[-3,0],[3,0],[2,6],[5,4],[0,9],[7,8]], r = 5 Output: 5 Explanation: Circle dartboard with center in (0,4) and radius = 5 contain all points except the point (7,8).
Constraints:
1 <= darts.length <= 100
darts[i].length == 2
-104 <= xi, yi <= 104
- All the
darts
are unique 1 <= r <= 5000
代码结果
运行时间: 109 ms, 内存: 17.6 MB
/*
* 思路:
* 1. 枚举每两个飞镖位置的组合,计算它们的几何中点作为圆心
* 2. 计算该几何中点距离两点的距离,确保它在半径范围内
* 3. 使用此圆心和半径计算在圆内的飞镖数量
* 4. 返回最大数量
*/
import java.util.*;
import java.util.stream.*;
public class DartboardStream {
public int numPoints(int[][] darts, int r) {
int n = darts.length;
return IntStream.range(0, n).boxed()
.flatMap(i -> IntStream.range(i + 1, n)
.mapToObj(j -> new int[][]{darts[i], darts[j]}))
.filter(pair -> Math.hypot(pair[0][0] - pair[1][0], pair[0][1] - pair[1][1]) <= 2 * r)
.flatMapToInt(pair -> IntStream.of(0, 1).map(k -> {
double midX = (pair[0][0] + pair[1][0]) / 2.0;
double midY = (pair[0][1] + pair[1][1]) / 2.0;
double angle = Math.atan2(pair[1][1] - pair[0][1], pair[1][0] - pair[0][0]);
double offset = Math.sqrt(r * r - (Math.hypot(pair[0][0] - pair[1][0], pair[0][1] - pair[1][1]) / 2) * (Math.hypot(pair[0][0] - pair[1][0], pair[0][1] - pair[1][1]) / 2));
double centerX = midX + offset * Math.cos(angle + (k == 0 ? Math.PI / 2 : -Math.PI / 2));
double centerY = midY + offset * Math.sin(angle + (k == 0 ? Math.PI / 2 : -Math.PI / 2));
return (int) Arrays.stream(darts)
.filter(dart -> Math.hypot(dart[0] - centerX, dart[1] - centerY) <= r)
.count();
}))
.max()
.orElse(1); // 至少一个飞镖在圆内
}
public static void main(String[] args) {
DartboardStream db = new DartboardStream();
int[][] darts = {{-2, 0}, {2, 0}, {0, 2}, {0, -2}};
int r = 2;
System.out.println(db.numPoints(darts, r)); // 输出4
}
}
解释
方法:
这道题的解决方案使用了角度扫描的方法。首先,对于每个点,计算它与其他所有点之间的角度,如果两点之间的距离小于等于直径,则这两点可以由同一个圆覆盖。基于这两点之间的中点和向量方向,我们可以计算出两个角度——入圈和出圈的角度。将这些角度事件存储起来,并进行排序。排序后,通过遍历这些角度事件,并用一个计数器来追踪当前圆内飞镖的数量,就可以找到最多的飞镖数量。通过比较所有点作为圆心时的最大值,得到最终答案。
时间复杂度:
O(n^3 log n)
空间复杂度:
O(n^2)
代码细节讲解
🦆
在计算两点之间的距离时,为什么要加上一个非常小的值`1e-8`?这对结果有什么影响?
▷🦆
为什么在处理角度事件时,需要将退出圆的事件设为负(`False`)?这样的设置有什么特定的理由或优势吗?
▷🦆
您提到的角度排序中使用了`lambda x: [x[0], -x[1]]`,这里为什么要对第二个元素使用负号进行排序?
▷