一、题目描述
假设海岸是一条无限长的直线,陆地位于海岸的一侧,海洋位于另外一侧。
每个小岛都位于海洋一侧的某个点上。
雷达装置均位于海岸线上,且雷达的监测范围为 d,当小岛与某雷达的距离不超过d时,该小岛可以被雷达覆盖。
我们使用笛卡尔坐标系,定义海岸线为x轴,海的一侧在 x 轴上方,陆地一侧在 x 轴下方。
现在给出每个小岛的具体坐标以及雷达的检测范围,请你求出能够使所有小岛都被雷达覆盖所需的最小雷达数目。
输入格式
- 第一行输入两个整数n和d,分别代表小岛数目和雷达检测范围。
- 接下来n行,每行输入两个整数,分别代表小岛的x,y轴坐标。
- 一行数据之间用空格隔开。
输出格式
- 输出一个整数,代表所需的最小雷达数目,若没有解决方案则所需数目输出“-1”。
数据范围
输入样例:
3 2
1 2
-3 1
2 1
输出样例:
2
二、题解
方法一:排序 + 勾股定理
思路
这道题是隐式区间贪心问题,但是题目没有说的那么直观,而是给出小岛坐标 (x, y)
与 检测范围的半径 d
,让读者计算区间并存储区间信息,下面给出思路。
由上图易得区间的开始与结束:
这样把区间画出来以后,问题可被简化为至少要多少个雷达才能将这些区间覆盖完?
- 我们将岛屿区间集合
segs[0...N]
按照end
升序排列。 - 因为我们是按照
end
升序,所以我们只需考虑下一个点的结尾位置是否能被当前雷达覆盖即可,具体为:- 如果下一个岛屿的
end
位置都能覆盖,证明前一个岛屿的end
位置肯定能被覆盖的了。 - 否则,证明需要新开一个雷达检测该岛屿,并将当前能被检测到的最远岛屿
Seg cur
指向到该岛屿。
- 如果下一个岛屿的
算法
- 如果某个坐标
y > d
,证明无论如何都无法检测到所有岛屿,print(-1)
- 如果下一个岛屿可以被当前雷达检测到,不用做任何动作,
continue
即可。 - 否则,
count++
表示新开一个雷达检测该岛屿。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int N = sc.nextInt();
int D = sc.nextInt();
Seg[] segs = new Seg[N];
for (int i = 0; i < N; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
if (D < y) {
System.out.println(-1);
return;
}
double sqrtX = Math.sqrt(D * D - y * y);
segs[i] = new Seg(x - sqrtX, x + sqrtX);
}
Arrays.sort(segs);
int count = 1;
Seg cur = segs[0];
for (int i = 1; i < N; i++) {
if (cur.end >= segs[i].begin) {
continue;
}
count++;
cur = segs[i];
}
System.out.println(count);
}
static class Seg implements Comparable<Seg>{
double begin, end;
public Seg(double _begin, double _end) {
begin = _begin;
end = _end;
}
@Override
public int compareTo(Seg other) {
return Double.compare(this.end, other.end);
}
}
}
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
值得注意的问题
- Q1:浮点数比较问题。
A1:用浮点数比较会存在误差,所以建议使用了 2 数之差小于某个精确值 exact value 来判断他们是否相等。double exv = 10^6; //相应地,if (cur.end >= segs[i].begin) 条件应该改为 if (cur.end > segs[i].begin + exv { continue; } 附:可以参考【蓝桥杯】赛前准备(二) 这篇文章中「杜绝浮点数比较的章节」
- Q2:实现
Comparable
接口后的如何编写返回值为int
类型的compareTo
方法问题。
A2:- 可以使用静态类
Double.compareTo(double d1, double d2)
- 转为对象然后调用自身的
compareTo
方法。Double obj1 = new Double("8.5"); Double obj2 = new Double("11.50"); int retval = obj1.compareTo(obj2);
- 可以使用静态类