pku 2187 Beauty Contest 凸包+旋转卡壳法
http://poj.org/problem?id=2187
给定平面上的一群点求任意两点的最远距离,如果枚举任意两点的话复杂度为o(n^2),在这里肯定超时。可以利用凸包将点的范围缩小到凸包的顶点上再循环枚举任意两点,这里最坏情况也为O(n^2)。不过这里貌似可以过的。另一种方法就是旋转卡壳法了,意思就是寻找任意边的最远点(凸包上的顶点),然后计算该点到两端点的最远距离即可。
旋转卡壳法 学习:http://www.cppblog.com/staryjy/archive/2009/11/19/101412.html
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const double eps = 1e-8; const int maxn = 50002; struct point { double x,y; }pt[maxn],stack[maxn]; int top; int cmp(point a,point b) { if (a.y != b.y) return a.y < b.y; else return a.x < b.x; } int dblcmp(double x) { if (x > eps) return 1; else if (x < -eps) return -1; else return 0; } double det(double x1,double y1,double x2,double y2) { return x1*y2 - x2*y1; } double cross(point a,point b,point c) { return det(b.x - a.x,b.y - a.y,c.x - a.x,c.y - a.y); } //这里起点与终点肯定在凸包上,不知道怎么证明 int graham(point *p,int len) { top = 0; int i; //先排序,lrj黑书上的排序方法 sort(p,p + len,cmp); stack[top++] = p[0]; stack[top++] = p[1]; //求右链 for (i = 2; i < len; ++i) { while (top > 1 && dblcmp(cross(stack[top - 2],stack[top - 1],p[i])) <= 0) top--; stack[top++] = p[i]; } //求左链 int tmp = top; for (i =len - 2; i >= 0; --i) { while (top > tmp && dblcmp(cross(stack[top - 2],stack[top - 1],p[i]))<= 0) top--; stack[top++] = p[i]; } return top - 1;//起点两次进栈 - 1 } int dis2(point a,point b) { double x = a.x - b.x; double y = a.y - b.y; return x*x + y*y; } int rotaing(point *p,int len) { p[len] = p[0]; int ans = 0,q = 1; for (int i = 0; i < len; ++i) { //直到出现他前边的点距离小于等于后边的点时说明该点是最远的点了。 while (cross(p[i],p[i + 1],p[q + 1]) > cross(p[i],p[i + 1],p[q])) q = (q + 1)%len; ans = max(ans,max(dis2(p[i],p[q]),dis2(p[i + 1],p[q + 1]))); //这里之所以计算i+1与q+1的距离是考虑到在凸多边形中存在平行边的问题 } return ans; } int main() { int n,i; scanf("%d",&n); for (i = 0; i < n; ++i) scanf("%lf%lf",&pt[i].x,&pt[i].y); int len = graham(pt,n); int ans = rotaing(stack,len); printf("%d\n",ans); return 0; }