解题思路:
以岛屿坐标为圆心,输入值为半径画圆,此圆会与 X 轴相交于 S、E 两点(岛屿 Y 坐标等于圆半径时,交一点;Y 坐标大于圆半径的情况提前排除,不参与后面的计算)。然后对由 S、E 组成的线段以 E 为判定标准排序,从左至右计算最少雷达个数。
吐个槽:POJ 落后于时代了,支持语言少,不支持 C++11 也就罢了,大多数题目时限竟然只有 1000ms。这道题我一开始是用 vector、cin/cout 等 C++ 常用特性做的,提交后竟然超时。无奈用 C 重写——改用 scanf/printf、静态创建数组等等。逻辑没变,再次提交,运行时间减为 16ms(这种情况已经是第二次碰到了,看来以后写 POJ 程序时得专门针对题目优化才行了)。让大家重视程序运行性能固然没错,但相信真正写程序的话没人会以静态创建数组的方法把数据的处理规模写死吧?我们不能为了做题而做题,对吧?
不啰嗦了,上代码。
#include <cstdio>
#include <algorithm>
#include <cmath>
// 岛屿对象
struct coordinate {
// 坐标、与 X 轴相交线段的起/终点
double x, y;
double s, e;
bool operator<(const coordinate &o) const {
return e < o.e;
}
} island[1001];
int solve(int n, int d) {
// 计算相交线段起/终点
for (int i = 0; i < n; ++i) {
double t = sqrt(d - island[i].y * island[i].y);
island[i].s = island[i].x - t;
island[i].e = island[i].x + t;
}
// 依线段终点排序
std::sort(island, island + n);
// 最少雷达数量(特殊情况已在输入时排除掉,
// 程序执行到这里时,至少存在一个合法岛屿,
// 故 ans 初始为 1)
int ans = 1;
double r = island[0].e;
// 从第二座岛屿开始扫描,如前一雷达设置点
// 覆盖不到,则雷达数加 1
for (int i = 1; i < n; ++i) {
if (island[i].s > r) {
++ans;
r = island[i].e;
}
}
return ans;
}
int main() {
int c = 1;
int n, d;
while (scanf("%d %d", &n, &d) && (n || d)) {
bool np = false;
for (int i = 0; i < n; ++i) {
// 坐标明明为整数,但用 scanf 时
// 后面计算时会出现精度不够的情况,
// 用 cin 则无此问题 -_-!!!
scanf("%lf %lf", &island[i].x, &island[i].y);
// 如果岛屿坐标超出雷达覆盖范围,
// 排除此情况,不参与计算
if (island[i].y > d) {
np = true;
}
}
if (np) {
printf("Case %d: %d\n", c, -1);
} else {
// 直接传入半径平方,减少重复计算
printf("Case %d: %d\n", c, solve(n, d * d));
}
++c;
}
return 0;
}