旋转卡壳
对踵点对定义:
如果过凸包上的两个点可以画一对平行直线,使凸包上的所有点都夹在两条平行线之间或落在平行线上,那么这两个点叫做一对对踵点。最远点对必然属于对踵点集(结论。可能是构成凸包最长的对角线一定是一对对踵点,不明。。。)
旋转卡壳寻找对踵点的基本思路是:
1.找到凸包最高点与最低点,作水平的一对平行线。规定一个时针方向两条线保持平行沿当前所在点转动。
2.当一条线与凸包的一条边重合时,记录重合边的另一个点与另一条线所在的点为一对对踵点。
3.重复第二步操作,直至重新回到最开始的一对对踵点
实现:
考虑一般情况是一条直线与一条边重合 ( i+1 -> i ),对于重合边靠右的一点i,根据对踵点的定义,一定在重边(i+1 -> i)与某一点 j 构成最大三角形是时有它的对踵点。记录i,j的距离,可以顺便记录点i+1与j的距离。j从0开始找,找到后再到下一条边i+1找与它对应的j,与他对应的j必然在当前j的位置或者它之后。这样就可以做到O(n)寻找,对踵点最多有3n/2对。
double rotat(Point ch[], int n)
{
int j = 0;
double ans = 0;
ch[n] = ch[0];
for(int i = 0; i < n; i++){
while(cross(ch[i], ch[i+1], ch[j]) < cross(ch[i], ch[i+1], ch[j+1]))
j = (j+1)%n;
ans = max(ans, max(dis2(ch[i], ch[j]), dis2(ch[i+1], ch[j])));
}
return ans;
}
POJ2781
#include <stack>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 5;
const double PI = acos(-1.0);
struct Point{
double x, y;
bool extreme;
int pos;
};
Point P[maxn];
int n;
Point TB[maxn];
int cntTB;
double dis(Point A, Point B)
{
return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
}
double dis2(Point A, Point B)
{
return (A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y);
}
double cross(Point O, Point A, Point B)
{
return (A.x - O.x)*(B.y - O.y) - (A.y - O.y)*(B.x - O.x);
}
bool cmp(Point A, Point B)
{
int cur = 0;
double temp = cross(P[cur], A, B);
if(temp > 0) return true;
else if(temp == 0 && (dis(P[cur], A) < dis(P[cur], B))) return true;
else return false;
}
bool ToLeft(Point O, Point A, Point B)
{
double temp = cross(O, A, B);
if(temp > 0) return true;
else if(temp == 0 && (dis(O, A) < dis(O, B))) return true;
else return false;
}
void Graham()
{
sort(P+1, P+n, cmp); ///extreme angle sort
stack<Point> s1, s2;
while(!s1.empty()) s1.pop();
while(!s2.empty()) s2.pop();
if(n == 1){
s1.push(P[0]);
}
else if(n == 2){
s1.push(P[0]);
s1.push(P[1]);
}
else{
s1.push(P[0]), s1.push(P[1]);
for(int i = n-1; i >=2; i--) s2.push(P[i]);
while(!s2.empty()){
Point B = s1.top();s1.pop();
Point A = s1.top();
Point C = s2.top();
B.extreme = false;
if(ToLeft(A, B, C)){
s2.pop();
s1.push(B), s1.push(C);
}
}
}
Point a[maxn];
int cnta = 0;
while(!s1.empty()){
a[cnta++] = s1.top();
s1.pop();
}
for(int i = cnta-1; i >= 0; i--)
TB[cntTB++] = a[i];
}
double rotat(Point ch[], int n)
{
int j = 0;
double ans = 0;
ch[n] = ch[0];
for(int i = 0; i < n; i++){
while(cross(ch[i], ch[i+1], ch[j]) < cross(ch[i], ch[i+1], ch[j+1]))
j = (j+1)%n;
ans = max(ans, max(dis2(ch[i], ch[j]), dis2(ch[i+1], ch[j])));
}
return ans;
}
int main()
{
while(~scanf("%d", &n)){
int tmp = 0;
cntTB = 0;
for(int i = 0; i < n; i++){
scanf("%lf%lf", &P[i].x, &P[i].y);
P[i].pos = i;
P[i].extreme = false;
if(P[i].y < P[tmp].y || (P[i].y == P[tmp].y && P[i].x < P[tmp].x))
tmp = i;
}
swap(P[tmp], P[0]);
Graham();
printf("%.f\n", rotat(TB, cntTB));
}
return 0;
}