题意:
给定n个点,现在要求找一个点作为一个半径为1的圆的圆心,使得这个圆能覆盖的点个数最多,输出最多能覆盖多少个点。
题解:
我们枚举两个点,认为他们是在这个圆上,那么就能确定圆心。之后可以从所有点到圆心的距离判断覆盖情况。
证:假设现在确定了一个能覆盖最多点的圆,那么我们可以移动这个圆使得覆盖的点数不变,且至少有两个点在这个圆上。
每两个点可以确定两个圆心,但只要用一个就够了(都用两点构成向量的上方圆心或者下方圆心)。
证明:若圆可以覆盖三个以上的点(两个点不需要找圆心。。),那么任意取覆盖的最外围三个点连线得到三角形ABC。我们枚举的时候枚举AB,AC,无论是先向量上方还是下方,都必定存在一个枚举的圆心,在三角形ABC内部。也就是说只要枚举一个圆心就能确定答案了(可以减少一半的时间消耗)。
注意:两点距离大于2的可以不枚举;初始化ans=1,否则一个点的时候会出错;距离判断时可以用距离的平方判断,因为sqrt很耗时间。
最后想说一句,那些耗时200MS的代码怎么写啊。。
代码:
#include <cstdio> #include <cstdlib> #include <cmath> #include <ctime> #include <cstring> #include <iostream> #include <algorithm> #include <map> #include <set> #include <queue> using namespace std; const int maxn=303; const double pi=atan(1.0)*4; const double len=1.0001*1.0001; //基础点和向量运算 struct Point{ double x,y; Point(double x=0,double y=0):x(x),y(y){} }; typedef Point Vector; Vector operator + (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);} Vector operator - (Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);} Vector operator * (Vector A,double p){return Vector(A.x*p,A.y*p);} Vector operator / (Vector A,double p){return Vector(A.x/p,A.y/p);} bool operator <(const Point& a, const Point& b) { return a.x<b.x||(a.x==b.x&&a.y<b.y); } const double eps=1e-6; int dcmp(double x)//判断正负,或者等于0 { if(fabs(x)<eps)return 0;else return x<0?-1:1; } bool operator==(const Point& a,const Point &b) { return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0; } double Dot(Vector A, Vector B){return A.x*B.x+A.y*B.y;}//点积 double Length(Vector A){return sqrt(Dot(A,A));}//OA长 double Angle(Vector A,Vector B){return acos(Dot(A,B)/Length(A)/Length(B));}//OA和OB的夹角 double Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}//叉积 double Area2(Point A,Point B,Point C){return Cross(B-A,C-A);}//三角形面积 Vector Rotate(Vector A,double rad)//rad为弧度,旋转rad度 { return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad)); } Vector Normal(Vector A)//A的单位法向量,A不能为零向量 { double L=Length(A); return Vector(-A.y/L,A.x/L); } Point e[maxn]; int ans; int get_cover_num(Point a,Point b,int n) { Point f1,f2,d1,d2,c; c=Point((a.x+b.x)/2,(a.y+b.y)/2); double L=Length(b-a); f1=Normal(b-a); f2=Normal(a-b); //找到圆心 d1=c+f1*sqrt(1.0-L*L/4); d2=c+f2*sqrt(1.0-L*L/4); int i,num1=0,num2=0; //省略一个圆心判断,可以减少一半的时间 for(i=0;i<n;i++) { if(dcmp(Dot(d1-e[i],d1-e[i])-len)<=0)num1++; //if(dcmp(Dot(d2-e[i],d2-e[i])-len)<=0)num2++; } return max(num1,num2); } int main() { int T; scanf("%d",&T); while(T--) { int i,j,k,n; scanf("%d",&n); for(i=0;i<n;i++) scanf("%lf%lf",&e[i].x,&e[i].y); ans=1; for(i=0;i<n;i++) { for(j=i+1;j<n;j++) { if(dcmp(Dot(e[i]-e[j],e[i]-e[j])-4.0)>0)continue; ans=max(ans,get_cover_num(e[i],e[j],n)); } } printf("%d\n",ans); } return 0; }