2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div.1) F - Distinct Distances
题意
在二维平面内给定(n(1leq nleq 40))个不同的点
对于平面内某个点(q),其与给定的(n)个点两两之间的距离构成的集合为
问如何选出点(q)才能求出这个集合元素个数的最小值,输出这个最小值
思路
可以发现,当(n=1)时答案一定为(1)
当(n=2)时,可以取两点连线的中垂线上任意一点,使得所有点到点(q)距离相等,答案为(1)
当(n=3)时,由于三个不共线的点可确定一个圆,所以当三点不共线时,(q)可以是这个圆的圆心,答案为(1);当三点共线,只能取某两个点中垂线上任意点,答案为(2)
当(ngt 3)时,可以发现
-
选择任意两点的中垂线上一点,总能对答案做出至少为(1)的贡献,总能保证答案(leq n-1)
-
选择任意不共线三点(如果存在)组成的圆的圆心,总能对答案做出至少为(2)的贡献,总能保证答案(nleq 2)
-
选择任意四点,组成两条线,在保证两条线不平行的前提下,选取两条线的两条垂线的交点作为点(q),总能对答案做出至少为(3)的贡献,总能保证答案(leq n-3)
由于“任意不共线三点组成的圆的圆心”可以由“任意四点组成两条线,其中两条线公用一点”推得,所以可以不用继续分类考虑
故选择的点(q)只可能是某四个点组成的两条不平行的线的中垂线的交点或者某两个点的中垂线上的点
对于后者,我们直接取中点进行计算即可
直接暴力枚举出点(q),对于集合内元素个数的计算,遍历一遍各个点求出(n)个距离,排序后稍微处理下即可
枚举加上遍历并排序,最后总时间复杂度为(O(n^5logn))
(判断平行与获取中垂线的方法没板子也可以直接推一下)
程序
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-10;
struct point
{
double x,y;
point(){}
point(double x,double y):x(x),y(y){}
}p[45];
struct line
{
point a,b;
line(){}
line(point a,point b):a(a),b(b){}
};
inline double Hypot(double x,double y) //直角三角形计算斜边,建议重写否则可能会TLE
{
return sqrt(x*x+y*y);
}
inline double getPointDis(point a,point b) //两点间距离
{
return Hypot(a.x-b.x,a.y-b.y);
}
bool isParallel(line la,line lb) //判断直线平行
{
point &u1=la.a,&u2=la.b,&v1=lb.a,&v2=lb.b;
if(u1.y==u2.y||v1.y==v2.y)
return u1.y==u2.y&&v1.y==v2.y;
return fabs((u1.x-u2.x)/(u1.y-u2.y)-(v1.x-v2.x)/(v1.y-v2.y))<eps;
}
line getPlumbLine(point a,point b) //获取中垂线
{
point p=point((a.x+b.x)/2.0,(a.y+b.y)/2.0);
if(fabs(a.x-b.x)<eps)
return line(p,point(p.x+1,p.y));
if(fabs(a.y-b.y)<eps)
return line(p,point(p.x,p.y+1));
double k=-1.0/((a.y-b.y)/(a.x-b.x));
return line(p,point(p.x+100,p.y+100.0*k));
}
point intersection(line la,line lb) //求两直线交点
{
point &u1=la.a,&u2=la.b,&v1=lb.a,&v2=lb.b;
double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))
/((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
return point(u1.x+(u2.x-u1.x)*t,u1.y+(u2.y-u1.y)*t);
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
if(n<3)
{
puts("1");
return 0;
}
else if(n==3)
{
if(isParallel(line(p[1],p[2]),line(p[1],p[3]))) //三点共线
puts("2");
else
puts("1");
return 0;
}
int ans=n;
double tmp[45];
for(int a=1;a<=n;a++) for(int b=a+1;b<=n;b++)
for(int c=1;c<=n;c++) for(int d=c+1;d<=n;d++)
{
if(isParallel(line(p[a],p[b]),line(p[c],p[d]))) //两线平行直接跳过
continue;
point pt=intersection(getPlumbLine(p[a],p[b]),getPlumbLine(p[c],p[d])); //获取中垂线交点
for(int i=1;i<=n;i++)
tmp[i]=getPointDis(pt,p[i]); //将所有距离求出
sort(tmp+1,tmp+1+n); //排序
int ansd=1;
for(int i=2;i<=n;i++)
if(tmp[i]-tmp[i-1]>=eps) //如果相邻两者差值大于eps,则算作两种
ansd++;
ans=min(ans,ansd);
}
for(int a=1;a<=n;a++) for(int b=a+1;b<=n;b++)
{
point pt=point((p[a].x+p[b].x)/2.0,(p[a].y+p[b].y)/2.0); //取中点
for(int i=1;i<=n;i++)
tmp[i]=getPointDis(pt,p[i]);
sort(tmp+1,tmp+1+n);
int ansd=1;
for(int i=2;i<=n;i++)
if(tmp[i]-tmp[i-1]>=eps)
ansd++;
ans=min(ans,ansd);
}
printf("%d
",ans);
return 0;
}