题解:使用平面凸包+旋转卡壳算法
平面凸包使用安德鲁算法:传送门
旋转卡壳:其实就是两个平行线正好把凸包卡住,其中卡着的点称为对踵点对.
很容易想象,某两个平行线对应的对踵点对最多只有四个。
那么怎么才能找到最远点对呢?
对于上图中的6个三角形的公共底边,可知当三角形的顶点在凸包上按逆时针旋转时,三角形的面积先由小变大再由大变小,即成单峰函数变化。所以在峰值时对应的凸包顶点距底边对应的两个对踵点的距离比其他情况大,这样,枚举每一个边并找到其面积单峰函数峰值对应的顶点,同时更新最大距离,旋转一趟下来便可以得到最后的结果。求面积单峰函数的峰值也很简单,就是通过叉积比较两个三角形的大小,当后者的面积比前者小时便找到了峰值,然后求距离更新最大值即可。
附上代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=50005;
struct point{
int x,y;
};
point points[maxn];
int stacks[maxn],top;
int n,start;
int distances(point a,point b)
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
int area(point p0,point p1,point p2)
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
bool cmp(point p1,point p2)
{
if(p1.x!=p2.x){
return p1.x<p2.x;
}else{
return p1.y<p2.y;
}
}
void andrew(int n)
{
sort(points,points + n,cmp);
for(int i = 0;i < n;i++){
while(top >= 2 && area(points[stacks[top - 2]],points[stacks[top - 1]],points[i]) <= 0){
top--;
}
stacks[top++] = i;
}
int temp = top + 1;
for(int i = n - 2;i >= 0;i--){
while(top >= temp && area(points[stacks[top - 2]],points[stacks[top - 1]],points[i]) <= 0){
top--;
}
stacks[top++] = i;
}
if(n > 1){
top--;
}
}
int rotatingCaliper()
{
int q = 1;
int ans = 0;
for(int i = 0;i < top;i++)
{
while(area(points[stacks[i]],points[stacks[i + 1]],points[stacks[q + 1]]) >
area(points[stacks[i]],points[stacks[i + 1]],points[stacks[q]]))
{
q = (q + 1) % top;
}
int d1 = distances(points[stacks[i]],points[stacks[q]]);
int d2 = distances(points[stacks[i + 1]],points[stacks[q]]);
ans = max(ans,max(d1,d2));
}
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF){
top=0;
for(int i=0;i<n;i++){
scanf("%d %d",&points[i].x,&points[i].y);
}
andrew(n);
int res=rotatingCaliper();
printf("%d\n",res);
}
return 0;
}