题目链接:http://poj.org/problem?id=1328
大致题意:给定一个直角坐标系,定义x轴为海岸线,海岸线上方是海,下方是陆地。在海域零星分布一些海岛, 需要要在海岸线上安装若干个雷达覆盖这些海岛。每个雷达的覆盖半径都是相同且固定。现在给定所有海岛的坐标(x,y), 以及雷达的覆盖半径d,求可以覆盖所有海岛的最小雷达数。若没有则输出-1
解题思路:
①只要存在任意一个海岛位置超出雷达最大覆盖半径(即y>d),则无解。
②当y<d时,以海岛坐标(x,y)为圆心,用雷达覆盖半径d画圆,根据前提条件可知,这个圆与x轴必定存在最少1个(y=d)、最多2个交点(y<d)。可以认为1个交点是2个交点重合的特殊情况,那么这2个交点在x轴上构成的线性区间,可以看作海岛的被覆盖范围在x轴上的投影 (由此就可以把二维的几何问题转化成一维几何问题)。
③按照所有海岛的x轴坐标,从小到大依次计算每个海岛在x轴上的投影区间(投影可能存在重叠),同时把每个雷达抽象成1个点,那么此问题就转化成:已知x轴上若干个区间(可能存在交集),现在要往这些区间内放若干个点,问怎样放置这些点,使得每个区间内至少存在一个点,且所放置的点的总量尽可能最少。
算法如下:
首先根据每个区间的左端点坐标对所有区间从左到右排序。
①在左起第一个区间A 的右端点 A.right 放置一个点,总放置点数 num+1
②若下一个区间B 的左端点 B.left > A.right, 说明 区间A 与 区间B 无交集,此时在区间B 的右端点 B.right 放置一个点,总放置点数 P+1 。否则说明 区间A 与 区间B 相交。此时进一步判断,若 B.right< A.right,说明 区间B 在 区间A 的内部,此时把原本放置在 A.right 的点移动到 B.right(确保区间B有一个点),总放置点数不变
③ 重复这个过程直到所有区间被遍历完成
PS:这里要注意使用scanf输入,而不要用cin输入,cin会超时。。。。
AC代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
struct Inteval {
double left;//区间左端点
double right;//区间右端点
};
bool cmp(Inteval a, Inteval b)
{
return a.left <= b.left;
}
int solve(Inteval *intevals, int nums)
{
int MinPoints = 1;
double right = intevals[0].right;
for(int i = 1; i < nums; i++) {
//区间i+1与区间i无交集
if(intevals[i].left > right) {
MinPoints++;
right = intevals[i].right;
}
//区间i+1在区间i内部//区间i+1与区间i相交
else if(intevals[i].right < right) {
right = intevals[i].right;
}
}
return MinPoints;
}
int main()
{
int islands, r, testCase = 1;
while((scanf("%d%d", &islands, &r) != EOF) && islands && r) {
double R = r * r;
Inteval *intevals = new Inteval[islands];
bool flag = true;//flag作为能否解决的标志
for(int i = 0; i < islands; i++) {
double x, y;
scanf("%lf%lf", &x, &y);
if(y > r) {
flag = false;
}
double offset = sqrt(R - y * y);
intevals[i].left = x - offset;
intevals[i].right = x + offset;
}
sort(intevals, intevals + islands, cmp);
int MinRadar = (flag ? solve(intevals, islands) : -1);
cout << "Case " << testCase++ << ": " << MinRadar << endl;
delete[] intevals;
}
return 0;
}