https://vjudge.net/problem/POJ-1328
一道比较基础的贪心题目, 比较巧妙的是题中用到了区间贪心的算法.
起初看题的时候一直有一个误区, 总想通过分析雷达关于岛的位置来确定范围, 后来发现其实加上一点儿逆向思维, 通过考虑岛来确定雷达的范围其实会更简单
具体来说, 考虑一下每个小岛被覆盖时雷达的区间, 最后就把各个小岛转化为了一个个区间, 我们的任务就是求最少的点, 使得所有的线段都被覆盖到, 直接顺序枚举+贪心即可
具体的贪心算法就是对于每个岛不断的和下一个岛判断是否有交集, 有的话再判断是否改变集合的边界(也就是真子集与否的判断)
//建立雷达 贪心
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn = 1010;
struct Island{
double left, right;
}isl[maxn];
bool operator < (const Island &a, const Island &b)
{
return a.left < b.left; //升序
}
int n, d, x, y;
int solve()
{
sort(isl+1, isl+1+n); //按左端点大小升序排列
//若当前线段与目前集合中的线段没有交集, 则加入新的雷达
double range = isl[1].right; int ans = 1;
for(int i = 2; i <= n; i++){
if(isl[i].left <= range) range = min(range, isl[i].right);
else range = isl[i].right, ans++;
}
return ans;
}
int main()
{
int index = 1;
while(scanf("%d%d",&n,&d)==2 && (n!=0||d!=0)){
bool flag = 1;
memset(isl, 0, sizeof(isl)); //初始化
for(int i = 1; i <= n; i++){
scanf("%d%d",&x,&y);
if(y > d) flag = 0;
isl[i].left = x - sqrt(d*d-y*y); //计算每个小岛决定的雷达范围
isl[i].right = x + sqrt(d*d-y*y); //勾股定理
}
if(flag) printf("Case %d: %d\n",index++, solve());
else printf("Case %d: -1\n",index++); //剪枝
}
return 0;
}