我直接说这道题题目重点:x轴是雷达所在位置,x轴以上是海,以下是陆地,海上有岛屿,雷达半径为r,问最少需要多少雷达可实现全覆盖岛屿。
错误的思路
这是我第一次想的方法,就是对每一个岛屿找到当前岛屿所能对应的雷达的最大值,再看有多少岛屿可以被覆盖,下一次从未被覆盖的第一个岛屿开始继续贪心。
但这种方法无法解决下面这个例子:
2 5
-5 3
-3 5
会发现第一个对应的最大雷达位置是-1,但是(-1,0)无法覆盖第二个点,但其实(-3,0)错过的这个点可以覆盖这两个点,所以这么简单的思路还是行不通。
但说实话,这种思路我个人觉得也是可以行得通,这么想,在每一个位置都判断所能覆盖的雷达个数,取个数最多的那个点,如果个数相同,取距离最远的那个点,但实现起来比较麻烦,所以下面这个思路简单又容易实现。
正确的思路:
可以逆向思维,通过每个岛屿所能被多大范围的雷达覆盖到,然后一个一个岛屿去与这个范围比较,如果有重叠则可以用一个雷达实现。
代码如下:
#include<iostream>
#include<algorithm>
#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)
//注意这里是min,而不是max,因为我们要的是交集,而不是并集
range = min(range, isl[i].right);
else{
range = isl[i].right;
ans++;
}
}
return ans;
}
int main(){
int k=1;
while(1){//n表示具体的点的个数,d表示覆盖距离
cin>>n>>d;
if(n==0&&d==0)
break;
bool flag = true;
for(int i = 1; i <= n; i++){
scanf("%d%d",&x,&y); //输入具体的坐标
if(y>d)
flag = false;//此处可以决定输出,即不可能实现的情况
if(flag){
isl[i].left = x*1.0-sqrt(d*d-y*y); //计算每个小岛决定的雷达范围(反向思考)
isl[i].right = x*1.0+sqrt(d*d-y*y); //勾股定理
}
}
if(flag)
//对于solve()函数的书写十分简单
cout<<"Case "<<k++<<": "<<solve()<<endl;//printf("Case %d: %d\n",k++, solve());
else
cout<<"Case "<<k++<<": -1"<<endl;//printf("Case %d: -1\n",k++); //不可能实现的情况
}
}