问题:在二维平面上给定n个点,求距离最远的两个点之间的距离是多少?
第一反应:
n
2
n^2
n2 暴力搞它. 然后瞄一眼范围, woc n=100000, 算了还是摸鱼吧.
几个小时月后, 还是学一下怎么求吧.
对于上面的问题, 我们进一步思考,这个最远点对一定会出现在这些点集的凸包上. 先求出这个点集的凸包, 然后用下面的算法就能在
n
l
o
g
n
nlogn
nlogn 的复杂度求出这个最远距离.
凸包的求法戳这: Graham-Scan算法 、Andrew算法
求出凸包后, 我们就可以在凸包上, 使用复杂度低至O(n)的旋转卡壳算法进行求解.
这个方法就叫做 “旋转卡壳”.
来跟我念:
x
u
a
ˊ
n
z
h
u
a
ˇ
n
q
i
a
ˇ
n
k
e
ˊ
\rm {xu\acute{a}n}\ \ {zhu\check an}\ \ {qi\check an}\ \ {k\acute e}
xuaˊn zhuaˇn qiaˇn keˊ
旋 转 卡 壳 \ 旋\ \ 转\ \ 卡\ \ 壳 旋 转 卡 壳
好吧其实我不知道这四个字怎么读, 听说有总共有十多种读法(逃
引入几个概念:
对踵点: 如果过凸多边形上两点作一对平行线, 使得整个多边形都在这两条线之间, 那么这两个点被称为一对对踵点. 可以证明对踵点的个数不超过3n/2个也就是说对踵点的个数是O(n)的.
如图:
凸多边形的直径: 即凸多边形上任意两个点之间距离的最大值. 直径一定会在对踵点中产生, 如果两个点不是对踵点, 那么两个点中一定可以让一个点向另一个点的对踵点方向移动使得距离更大. 并且点与点之间的距离可以体现为线与线之间的距离, 在非对踵点之间构造平行线, 一定没有在对踵点构造平行线优, 这一点可以通过平移看出.
然后我们就要从所有的对踵点中找到距离最大的一对. 由于两个点不好找, 我们可以换一种找的方法, 如图:
枚举每一条边, 然后找到距离这条边最远的点. 在这种情况下我们发现, 一个对踵点到对应边之间的距离比其他点要大. 也就是说, 一个对踵点和对应边所形成的三角形的面积是最大的,所以我们可以使用求叉积的方法——求三角形面积来求解对踵点。
凸包上的点依次与对应边产生的距离成单峰函数, 面积上升到最高点后, 又会下降. 利用这一性质, 我们可以根据凸包上点的顺序, 枚举对踵点, 直到下一个点的距离小于当前点为止, 而且随着对应边的旋转, 最远点也只会顺着这个方向旋转, 我们可以从上一次的对踵点开始继续寻找这一次的.
神图:
代码:
//距离的平方
int dist2(Point a,Point b)
{
return (a-b)*(a-b);
}
//旋转卡壳,求两点间距离平方的最大值
int rotating_calipers(Point p[],int n)//点的存储[0,n)
{
int ans = 0;
Point v;
int cur = 1;
p[n]=p[0];
for(int i = 0; i < n; i++)
{
while(((p[cur]-p[i+1])^(p[i]-p[i+1]))<((p[cur+1]-p[i+1])^(p[i]-p[i+1]))){
cur=(cur+1)%n;
}//比较两个三角形面积,直到下一个点的距离小于当前点为止, 可改为下面的注释的写法
//上面式子化简后的, 计算少一次叉积.
//v = p[i]-p[i+1];
//while((v^(p[cur+1]-p[cur])) < 0)
// cur = (cur+1)%n;
ans = max(ans,max(dist2(p[i],p[cur]),dist2(p[(i+1)%n],p[cur+1])));
}
return ans;
}