题意:给定n(2<=n<=50000)对整数点,求距离最远的一对点(即最远点对)的距离的平方。
题解:
1)先确定最远点对必定是凸包上的一对点。对于一个三个点组成的三角形,其内部任意点肯定不属于最远点对。证明如下:
*这个点到三角形三点必定比最长的三角形边短。
*这个点A与与三角形外部点B相连时,过这个点作连线的垂线,垂线必定与三角形有交点,即在BA方向,垂线外必定存在一个三角形上的点C,使得BC>AB。
而在凸包中,任意点必定可以找到一个由凸包上的点构成的三角形,使得该点在三角形内(对于m凸包,可以分成m-2个三角形,覆盖整个凸包)。
2)通过旋转卡壳法找到最远点对。
旋转卡壳法就是模拟游标卡尺旋转凸包的方法,先随意卡住两点,然后旋转,当找到更远的点对时,卡尺会被撑开,旋转一周后,卡尺的宽度就是最远点对的距离。(什么是游标卡尺?自己百度。。)
详细的解释:
过最远的点对作连线的垂线,两条线平行,且不与凸包上有交点(如果有交点,就说明存在更远的点,那么最远点对就不成立)。反过来说就是所有可以作这两条不 与凸包相交平行垂线的点对中必定存在最远点对。现在就是要枚举这些可以作平行垂线的点对即可。而且由上述解释可以得出,每个点相对应这个点的最远点有且只有一 个,那么枚举的时候就可以只枚举一半的点就可以了。
现在就是找必定可以作平行垂线的两个点(例如:横坐标最小和最大的两个点),然后两条线平行逆时针旋转,便利所有点,就可以得到所有点对了。在旋转过程中, 只有平行线与凸包的边相重合才会变换点,所以可以用叉积确定先过那条边Cross(ch[(i+1)%m]-ch[i],ch[(j+1)%m]-ch[j])<0(通过叉积正负,我们可以知道那条平线先到边 上,从而变化对应点。)
代码:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
using namespace std;
//基础点和向量运算
struct Point{
int x,y;
Point(int x=0,int 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);
}
int Cross(Vector A,Vector B){return A.x*B.y-A.y*B.x;}//叉积
int dis(Vector A,Vector B)//距离的平方
{
return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}
const int maxn=5e4+10;
int n;
Point p[maxn],ch[maxn];
int ConvexHull()//求凸包
{
sort(p,p+n);
int i,m=0,k;
for(i=0;i<n;i++)
{
while(m>1&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--;
ch[m++]=p[i];
}
k=m;
for(i=n-2;i>=0;i--)
{
while(m>k&&Cross(ch[m-1]-ch[m-2],p[i]-ch[m-2])<=0)m--;
ch[m++]=p[i];
}
if(n>1)m--;
return m;
}
void solve(int m)//用旋转卡壳法求最长点对
{
if(m==2)
{
printf("%d\n",dis(ch[0],ch[1]));
return;
}
int i,j,k;
i=j=0;
for(k=0;k<m;k++)
{
if(ch[i].x>ch[k].x)i=k;
if(ch[j].x<ch[k].x)j=k;
}
int res=0,si=i,sj=j;
//printf("%d %d\n",i,j);
while(i!=sj||j!=si)
{
res=max(res,dis(ch[i],ch[j]));
if(Cross(ch[(i+1)%m]-ch[i],ch[(j+1)%m]-ch[j])<0)i=(i+1)%m;
else j=(j+1)%m;
}
printf("%d\n",res);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int i,j,k,m;
for(i=0;i<n;i++)
scanf("%d%d",&p[i].x,&p[i].y);
m=ConvexHull();
//for(i=0;i<m;i++)
// printf("*%d %d\n",ch[i].x,ch[i].y);
solve(m);
}
return 0;
}