题目:http://poj.org/problem?id=2187
/*经典题目:凸包加旋转卡壳
凸包第一题,由于本人的粗心,T了很久,最终发现是旋转卡壳部分写错了,不过学会了旋转卡壳的两种姿势
先求凸包,然后旋转卡壳求出凸包直径即可
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
const int N = 50010;
int n;
struct P
{
int x, y;
P(){}
P(int x, int y) : x(x), y(y) {}
P operator- (P p)
{
return P(x - p.x, y - p.y);
}
int dot(P p) //向量点乘
{
return x * p.x + y * p.y;
}
int det(P p) //向量叉乘
{
return x * p.y - p.x * y;
}
int len(P p)
{
return (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y);
}
}ps[N], qs[N];
bool cmp(P a, P b)
{
if(a.x == b.x) return a.y < b.y;
else return a.x < b.x;
}
int graham() //求凸包
{
int k = 0;
for(int i = 0; i < n; i++) //求凸包下半侧
{
while(k > 1 && (qs[k-1] - qs[k-2]).det(ps[i] - qs[k-1]) <= 0) k--;
qs[k++] = ps[i];
}
for(int i = n - 2, t = k; i >= 0; i--) //求凸包上半侧
{
while(k > t && (qs[k-1] - qs[k-2]).det(ps[i] - qs[k-1]) <= 0) k--;
qs[k++] = ps[i];
}
return k - 1;
}
int rotating_calipers(int m) //旋转卡壳
{
if(m == 2) //此种方法必须特判m为2时,不然会T
return qs[0].len(qs[1]);
int i = 0, j = 0;
for(int k = 0; k < m; k++) //找出最左点和最有点,即x轴方向上的对蹱点
{
if(! cmp(qs[i], qs[k])) i = k; //最左点
if(cmp(qs[j], qs[k])) j = k; //最右点
}
int si = i, sj = j, res = 0;
while(i != sj || j != si) //将方向逐步旋转180度
{
res = max(res, qs[i].len(qs[j]));
/*判断先转到边i-(i+1)的法线方向还是j-(j+1)的法线方向*/
if((qs[(i+1)%m] - qs[i]).det(qs[(j+1)%m] - qs[j]) < 0) i = (i + 1) % m; //先转到边i-(i+1)的法线方向
else j = (j + 1) % m; //先转到边j-(j+1)的法线方向
}
return res;
}
/*
int rotating_calipers(int m) //旋转卡壳
{
int j = 1, res = 0;
qs[m] = qs[0];
for(int i = 0; i < m; i++) //依次用叉积找出凸包每一条边对应的最高点
{
//同底不同高,每次用面积判断高的大小就可以了
while((qs[i+1] - qs[i]).det(qs[j+1] - qs[i]) > (qs[i+1] - qs[i]).det(qs[j] - qs[i]))
j = (j + 1) % m;
res = max(res, max(qs[j].len(qs[i]), qs[j+1].len(qs[i+1]))); //每条底上有两个点,所以要 max 两次
}
return res;
}
*/
int main()
{
while(~ scanf("%d", &n))
{
for(int i = 0; i < n; i++)
scanf("%d%d", &ps[i].x, &ps[i].y);
sort(ps, ps + n, cmp);
int m = graham();
printf("%d\n", rotating_calipers(m));
}
return 0;
}