题目链接:http://poj.org/problem?id=2187
求平面上距离最远的两个点可以用旋转卡壳来做。
如果qa,qb是凸包上最远两点,必然可以分别过qa,qb画出一对平行线。通过旋转这对平行线,我们可以让它和凸包上的一条边重合,如图中蓝色直线,可以注意到,qa是凸包上离p和qb所在直线最远的点。于是我们的思路就是枚举凸包上的所有边,对每一条边找出凸包上离该边最远的顶点,计算这个顶点到该边两个端点的距离,并记录最大的值。直观上这是一个O(n2)的算法,和直接枚举任意两个顶点一样了。但是注意到当我们逆时针枚举边的时候,最远点的变化也是逆时针的,这样就可以不用从头计算最远点,而可以紧接着上一次的最远点继续计算。于是我们得到了O(n)的算法。
//计算凸包直径,输入凸包ch,顶点个数为n,按逆时针排列,输出直径的平方 int rotating_calipers(Point *ch,int n) { int q=1,ans=0; ch[n]=ch[0]; for(int p=0;p<n;p++) { while(det(ch[p+1],ch[q+1],ch[p])>det(ch[p+1],ch[q],ch[p])) q=(q+1)%n; ans=max(ans,max(dist2(ch[p],ch[q]),dist2(ch[p+1],ch[q+1]))); } return ans; }其中det函数是计算叉积,可以想成是计算三角形面积,因为凸包上距离一条边最远的点和这条边的两个端点构成的三角形面积是最大的。之所以既要更新(ch[p],ch[q])又要更新(ch[p+1],ch[q+1])是为了处理凸包上两条边平行的特殊情况。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <queue> #include <map> #include <algorithm> #include <iostream> using namespace std; #define Maxn 50005 const double eps = 1e-8; struct Point { int x; int y; Point() {} Point(double _x,double _y):x(_x),y(_y) {} friend Point operator + (Point a,Point b) { return Point(a.x+b.x , a.y+b.y); } friend Point operator - (Point a,Point b) { return Point(a.x-b.x , a.y-b.y); } bool operator < (const Point &a)const { return y<a.y || (y == a.y && x<a.x); } }p[Maxn],stack[Maxn]; int dcmp(double x) //三态函数 { if(fabs(x)<eps)//在一定的精度范围内可认为是0 return 0; return x>0?1:-1; } double det(Point a,Point b) // 叉积,重载叉积函数 { return a.x*b.y-a.y*b.x; } double det(Point a,Point b,Point o) // 叉积 { return det(a-o,b-o); } double det(Point a,Point b,Point c,Point d) // 叉积 { return det(b-a,d-c); } double dot(Point a,Point b) // 点积 { return a.x*b.x + a.y*b.y; } double dot(Point a,Point b,Point o) // 点积 { return dot(a-o,b-o); } int dist2(Point a,Point b) { return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y); } //计算凸包直径,输入凸包ch,顶点个数为n,按逆时针排列,输出直径的平方 int rotating_calipers(Point *ch,int n) { int q=1,ans=0; ch[n]=ch[0]; for(int p=0;p<n;p++) { while(det(ch[p+1],ch[q+1],ch[p])>det(ch[p+1],ch[q],ch[p])) q=(q+1)%n; ans=max(ans,max(dist2(ch[p],ch[q]),dist2(ch[p+1],ch[q+1]))); } return ans; } //水平序求凸包 int grahamScan(int n) { int top = -1; stack[++top] = p[0]; stack[++top] = p[1]; for(int i=2;i<n;i++) { while(top && det(stack[top],p[i],stack[top-1])<=0) { top--; } stack[++top] = p[i]; } int midtop = top; for(int i=n-2;i>=0;i--) { while(top>midtop && det(stack[top],p[i],stack[top-1])<=0) { top--; } stack[++top] = p[i]; } return top; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #endif int n; while(scanf(" %d",&n)!=EOF) { for(int i=0;i<n;i++) { scanf(" %d %d",&p[i].x,&p[i].y); } sort(p,p+n); int top = grahamScan(n); int ans = rotating_calipers(stack,top); printf("%d\n",ans ); } return 0; }