思路:以岛屿为圆心作半径为d的圆,则每个可以被覆盖的岛屿(即y<=d)都可以在x轴上形成一个或两个交点。也就是说,每个岛屿在画圆后都在x轴上形成一个长度大于或等于0的区间。这些区间即为安装雷达的地方。接下来要做的就是将这些区间的相交区间(即交集)找出来并计数。相交区间的数目即为雷达的数目。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <climits>
#include <cmath>
using namespace std;
typedef struct{
double x;
double y;
}island;
typedef struct{
double L;
double R;
}interval;
island a[1010];
interval b[1010];
bool cmp(const interval &a,const interval &b)
{
return a.L<b.L;
}
int num;
int main()
{
double d;
int n,test;
int no;
int i,j;
int ans;
for(test=1,num=0;scanf("%d %lf",&n,&d)!=EOF;test++)
{
ans=0;no=0;
int flag=0;
if(!n&&!d)
break;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(i=1;i<=n;i++){
scanf("%lf %lf",&a[i].x,&a[i].y);
if(a[i].y>d){
flag=1;
}
}
if(flag){
printf("Case %d: -1\n",test);
continue;
}
for(i=1;i<=n;i++){
b[i].L=a[i].x-sqrt(d*d-((a[i].y)*(a[i].y)));
b[i].R=a[i].x+sqrt(d*d-((a[i].y)*(a[i].y)));
}
sort(b+1,b+n+1,cmp);
for(i=1,ans=0;i<=n;i++){ //i++:当找到一个相交区间后,查找下一个相交区间的起始左端点值即为上一个相交区间(就是已经被找到了的那个)的左端点值的下一个
ans++;
double right=b[i].R;
for(j=i+1;j<=n;j++){
if(b[j].L<=right){
if(b[j].R<right){
right=b[j].R; //将右端点左移
}
i++; //将左端点右移
}else{
break;
}
}
}
printf("Case %d: %d\n",test,ans);
}
return 0;
}
找相交区间部分的讲解:找相交区间就是要找到尽可能大的左端点和右端点。先将结构数组b[]按每个区间的左端点大小升序排列,这样是为了找到尽可能大的左端点。每次循环中同时将左端点右移、右端点左移直到左端点大于右端点。因为每一轮循环都必定能找到一个相交区间,故每一轮循环都要ans++。
原做法:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <climits>
#include <cmath>
using namespace std;
typedef struct{
double x;
double y;
}island;
island a[1010];
bool cmp(const island &a,const island &b)
{
return a.x<b.x;
}
bool tag[1010];
int num;
int main()
{
double d;
int n,test;
int no;
int i;
int ans;
for(test=1,num=0;scanf("%d %lf",&n,&d)!=EOF;test++)
{
no=0,ans=0;
memset(a,0,sizeof(a));
if(!n&&!d) break;
for(i=1;i<=n;i++)
{
scanf("%lf %lf",&a[i].x,&a[i].y);
if(a[i].y>d)
no=1;
}
if(no)
{
printf("Case %d: -1\n",test);
continue;
}
getchar();
//每次都以最左边的为被圈入圆圈中的岛屿为参考来确定安装点的坐标,直到全部都被覆盖完
sort(a+1,a+n+1,cmp);
int st=1,cov;
double xtmp;
for(cov=0;cov!=n;){
xtmp=a[st].x+sqrt(d*d-(a[st].y)*(a[st].y));
for(i=st;i<=n;i++){
if(((xtmp-a[i].x)*(xtmp-a[i].x)+(a[i].y)*(a[i].y))>d*d){
st=i;
break;
}
tag[i]=true;
cov++;
}
ans++;
}
printf("Case %d: %d\n",test,ans);
}
return 0;
}
这个做法的思路是每次都以最左边的未被覆盖的岛屿来确定雷达的坐标(即刚好能覆盖最左边的这个岛屿),循环地去做,直到所有的岛屿都被覆盖。但这样做的问题在于,并不一定能尽可能多地覆盖岛屿。比如说这组数据:
2 3 0 2 1 3
正确输出是1,但用这种方法做出来的答案是2。以最左边的未被覆盖的点(0,2)为基准确定雷达坐标后,雷达并不能把(1,3)覆盖。但实际上,只需要一个在x=1上的雷达就可以将两个岛屿覆盖。