POJ 2187 Beauty Contest 构造凸包 + 旋转卡壳
在刷了一整版的 TLE 和 WA 外加一个CE和几个OLE之后终于拿到了第一个旋转卡壳的AC。。。泪奔
1978年, M.I. Shamos's Ph.D. 的论文"Computational Geometry"标志着计算机科学的这一领域的诞生。 当时他发表成果的是一个寻找凸多边形直径的一个非常简单的算法, 即根据多边形的一对点距离的最大值来确定。
后来直径演化为由一对对踵点对来确定。 Shamos提出了一个简单的 O(n) 时间的算法来确定一个凸 n 角形的对踵点对。 因为他们最多只有 3n/2 对, 直径可以在 O(n) 时间内算出。
如同Toussaint后来提出的, Shamos的算法就像绕着多边形旋转一对卡壳。 因此就有了术语“旋转卡壳”。 1983年, Toussaint发表了一篇论文, 其中用同样的技术来解决许多问题。 从此, 基于此模型的新算法就确立了, 解决了许多问题。
他们包括:
一个多边形直径的简单例子如左图所示。 直径点对在图中显示为被平行线穿过的黑点 (红色的一对平行线). 直径用浅蓝色高亮显示。
begin p0:=pn; q:=NEXT[p]; while (Area(p,NEXT[p],NEXT[q]) > Area(p,NEXT[p],q)) do q:=NEXT[q]; q0:=q; while (q != p0) do begin p:=NEXT[p]; Print(p,q); while (Area(p,NEXT[p],NEXT[q]) > Area(p,NEXT[p],q) do begin q:=NEXT[q]; if ((p,q) != (q0,p0)) then Print(p,q) else return end; if (Area(p,NEXT[p],NEXT[q]) = Area(p,NEXT[p],q)) then if ((p,q) != (q0,p0)) then Print(p,NEXT[q]) else Print(NEXT[p],q) endend.
Print(p,q)
表示将 (p,q) 作为一个对踵点对输出, Area(p,q,r)
表示三角形 pqr 的有向面积。 #include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>#include <queue>#include <cmath>#include <algorithm>using namespace std;struct P{ double x,y,a;}p[50010],s[50010],re;double calangle(P a,P b){ if(a.x == b.x && a.y == b.y) return -2; return acos( (b.x-a.x)/(2*sqrt((b.x-a.x)*(b.x-a.x) + (b.y-a.y)*(b.y-a.y) )) );}double calangle(P v1,P a,P b){ P v2; v2.x = b.x-a.x; v2.y = b.y-a.y; return acos( (v1.x*v2.x + v1.y*v2.y)/(2*sqrt((v1.x*v1.x + v1.y*v1.y)*(v2.x*v2.x + v2.y*v2.y))) );}bool cmp(P p1,P p2){ if(p1.a < p2.a) return true; if(p1.a == p2.a && p1.y < p2.y) return true; if(p1.a == p2.a && p1.y == p2.y && p1.x < p2.x) return true; return false;}bool judgesite(P p1,P p2,P p3){ P t1,t2; t2.x = p2.x-p1.x; t2.y = p2.y-p1.y; t1.x = p3.x-p1.x; t1.y = p3.y-p1.y; if(t2.x*t1.y - t1.x*t2.y > 0)//此处 >= 0 将会添加共线点 return true; return false;}int top;double CalMaxDiameter(P *p,int n){ // p 为凸包上点 逆时针排列 // n 为数量 P p1,p2;//两个对踵点 P v1 = {0,-1,0},v2 = {0,1,0};//两个射线 int s1,s2;//两个对重点的坐标 int n1,n2; double a1,a2; double MaxDiameter = -1,TempDiameter;//记录最大直径 p1.x = 1000000; p2.x = -1000000; //寻找第一对对踵点 最左边的 和 最右边的肯定是一对对踵点 for(int i = 0;i < n; ++i) { if(p[i].x > p2.x || (p[i].x == p2.x && p[i].y > p2.y)) { s2 = i; p2 = p[i]; } if(p[i].x < p1.x || (p[i].x == p1.x && p[i].y < p1.y)) { s1 = i; p1 = p[i]; } } MaxDiameter = (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y); n1 = s1; n2 = s2; while( (n1 != s2 || n2 != s1)) { a1 = calangle(v1,p[n1],p[(n1+1)%n]); a2 = calangle(v2,p[n2],p[(n2+1)%n]); if(a1 < a2) { v1.x = p[(n1+1)%n].x - p[n1].x; v1.y = p[(n1+1)%n].y - p[n1].y; v2.x = -v1.x; v2.y = -v1.y; n1 = (n1+1)%n; } else { v2.x = p[(n2+1)%n].x - p[n2].x; v2.y = p[(n2+1)%n].y - p[n2].y; v1.x = -v2.x; v1.y = -v2.y; n2 = (n2+1)%n; } TempDiameter = (p[n1].x - p[n2].x)*(p[n1].x - p[n2].x) + (p[n1].y - p[n2].y)*(p[n1].y - p[n2].y); if(MaxDiameter < TempDiameter) MaxDiameter = TempDiameter; } return MaxDiameter;}int main(){ int n; int i; while(scanf("%d",&n) != EOF) { for(i = 0;i < n; ++i) { scanf("%lf %lf",&p[i].x,&p[i].y); } //寻找参考点 for(re = p[0],i = 1; i < n; ++i) { if(re.y > p[i].y || (re.y == p[i].y && re.x > p[i].x)) re = p[i]; } //计算p[i] 与 参考点 与 x轴正方向形成的夹角 for(i = 0;i < n; ++i) { p[i].a = calangle(re,p[i]); } sort(p,p+n,cmp); //去重点 top = 0; for(s[top++] = p[0],i = 1;i < n; ++i) { if(p[i].x != p[i-1].x || p[i].y != p[i-1].y) s[top++] = p[i]; } for(i = 0;i < top; ++i) { p[i] = s[i]; } n = top; top = 0; s[top++] = p[0]; s[top++] = p[1]; for(i = 2;i < n;) { if(top < 2 || judgesite(s[top-2],s[top-1],p[i])) { s[top++] = p[i]; ++i; } else top--; } printf("%d\n",(int)CalMaxDiameter(s,top)); } return 0;}