本题的一个重要转化是:将每小岛的位置转换成一个线段,然后求能够使得每个线段上都有一个点的情况下,需要的点的最小个数。因为有重叠部分的线段只需要一个点就可以,所以重点在于求线段的交集。
对于一组线段组成的集合,当且仅当 这个集合中最大的左端点小于最小的右端点的时候,这一组线段集合才会都重叠。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int MAX = 1010;
int n, d;
int tot = 1;
struct Node{
double left;
double right;
};
Node a[MAX];
bool cmp(const Node& a, const Node& b)
{
return a.left < b.left;
}
void solve()
{
// 按左端点从小到大排序
sort(a, a+n, cmp);
// 遍历
int ans = 1;
double now_right = a[0].right;
for (int i = 1; i < n; ++i) {
//所有线段的交集就是最大的左端点小于最小的右端点
// 如果现在的左端点小于等于现存集合中线段的最小右端点,说明这个集合可以加入这个线段
if (a[i].left <= now_right) {
now_right = min(now_right, a[i].right);
}
// 如果现在的集合不能加入这个线段,那么就新开一个集合,其最小右端点就是该线段的右端点
else {
ans++;
now_right = a[i].right;
}
}
cout << "Case " << tot << ": " << ans << endl;
tot++;
}
int main()
{
while(cin >> n >> d) {
bool flag = true;
if (n == 0 && d == 0) break;
for (int i = 0; i < n; ++i) {
int x, y;
cin >> x >> y;
// 如果坐标y大于d,那么无解
if (y > d) {
flag = false;
}
// 计算能够覆盖该点的圆心所在的线段
a[i].left = (double)x - sqrt((double)d*d - (double)y*y);
a[i].right = (double)x + sqrt((double)d*d - (double)y*y);
}
// 有解的话就solve
if (flag) solve();
// 无解就直接print -1
else {
cout << "Case "<< tot << ": "<< -1 << endl;
tot++;
}
}
return 0;
}