模板:洛谷P2742 二维凸包 / Fencing the Cows
给你一团点,让你找到一种方法,用最短的连线把这些点包起来
e.g:
求凸包有许多种方法。这里介绍极点排序法。
极点排序是啥捏?就是在给你的那团点中找到一个纵坐标最小的点,以那个点作为极点,求出每一个点到极点的连线与水平线的夹角,按这个角度排序。
排完序过后就好办了。按顺序试着把排序后的点加入一个单调栈。(栈顶指针为top)
0.压入1、2号店
1.取下一个点P
2.判断 stack[top-1]->stack[top],stack[top]->P 的路径拐向
3.如果左拐,压入,回到步骤1
4.如果右拐,弹出栈顶元素,回到步骤2
拐向:比如P1->P2,P2->P3,如果P2-P3这条线段相对于P1-P2顺时针旋转了,就是“左拐”。如果逆时针旋转了,就是“右拐”。
最后,把最后一个点连回极点按上面的步骤操作一下,单调栈中剩余的元素就是凸包了
#include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<ctime> using namespace std; const double EPS=pow(10,-8),INF=pow(10,14); bool IsEqual(double x,double y){ return x-EPS<=y&&x+EPS>=y; } //Point struct Vec2{ double x,y; Vec2 operator+(Vec2 P){return (Vec2){x+P.x,y+P.y};} Vec2 operator-(Vec2 P){return (Vec2){x-P.x,y-P.y};} double operator*(Vec2 P){return x*P.x+y*P.y;}//点乘 double operator^(Vec2 P){return x*P.y-P.x*y;}//(伪)叉乘 }; double Dis(Vec2 A,Vec2 B){ return sqrt(pow(A.x-B.x,2)+pow(A.y-B.y,2)); } Vec2 pt[50000],hull[50000]; int n,hn; bool AngleCmp(Vec2 x,Vec2 y){//通过叉积比较极角大小 double t=(x-pt[1])^(y-pt[1]); if(t>0) return 1; else if(t<0) return 0; else return Dis(pt[1],x)<Dis(pt[1],y);//极角相同极点距小的优先 } void GetHull(){ pt[n+1]=pt[1];//最后要连向极点 hn=0; hull[++hn]=pt[1]; hull[++hn]=pt[2]; for(int i=3;i<=n+1;i++){ while(((hull[hn]-hull[hn-1])^(pt[i]-hull[hn]))<=0)//不选共线点 hn--; hull[++hn]=pt[i]; } hn--; } int main(){ cin>>n; for(int i=1;i<=n;i++){ scanf("%lf%lf",&pt[i].x,&pt[i].y); if((pt[i].y<pt[1].y)||(pt[i].y==pt[1].y&&pt[i].x<pt[1].x)) swap(pt[1],pt[i]); //把y最小(x最小)的点换到pt[1] } sort(pt+2,pt+1+n,AngleCmp);//极点排序 GetHull(); double ans=0; for(int i=1;i<=hn;i++)//算周长 ans+=Dis(hull[i],hull[i+1]); printf("%.2lf",ans); return 0; }
bsoj1773 最远距离点对(poj2187)
给定平面上的n个点,找出它们之间最远的点对。
容易发现这对点一定在凸包上。先求得凸包,在凸包上求出最远点即可。
怎么求?暴力...可能被卡到O(n^2)
后来发现距离是单峰的可以O(nlogn)三分求最大值,但懒得写...
有个叫旋转卡壳的算法可以O(n)实现,但调了半天没调出来...以后再研究了
#include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<ctime> using namespace std; const double EPS=pow(10,-8),INF=pow(10,14); bool IsEqual(double x,double y){ return x-EPS<=y&&x+EPS>=y; } //Point struct Vec2{ double x,y; Vec2 operator+(Vec2 P){return (Vec2){x+P.x,y+P.y};} Vec2 operator-(Vec2 P){return (Vec2){x-P.x,y-P.y};} double operator*(Vec2 P){return x*P.x+y*P.y;}//点乘 double operator^(Vec2 P){return x*P.y-P.x*y;}//(伪)叉乘 }; double Dis(Vec2 A,Vec2 B){ return sqrt(pow(A.x-B.x,2)+pow(A.y-B.y,2)); } Vec2 pt[50000],hull[50000]; int n,hn; bool AngleCmp(Vec2 x,Vec2 y){//通过叉积比较极角大小 double t=(x-pt[1])^(y-pt[1]); if(t>0) return 1; else if(t<0) return 0; else return Dis(pt[1],x)<Dis(pt[1],y);//极角相同极点距小的优先 } void GetHull(){//求凸包 pt[n+1]=pt[1];//最后要连向极点 hn=0; hull[++hn]=pt[1]; hull[++hn]=pt[2]; for(int i=3;i<=n+1;i++){ while(((hull[hn]-hull[hn-1])^(pt[i]-hull[hn]))<=0)//不选共线点 hn--; hull[++hn]=pt[i]; } hn--; } double GetMaxPtPairDis(){//暴力求最远点对 double ans=-INF; for(int i=1;i<=hn;i++){ for(int j=1;j<=hn;j++){ double dis=Dis(hull[i],hull[j]); if(dis>ans) ans=dis; } }return ans; } int main(){ while(cin>>n){ for(int i=1;i<=n;i++){ scanf("%lf%lf",&pt[i].x,&pt[i].y); if((pt[i].y<pt[1].y)||(pt[i].y==pt[1].y&&pt[i].x<pt[1].x)) swap(pt[1],pt[i]); //把y最小(x最小)的点换到pt[1] } sort(pt+2,pt+1+n,AngleCmp);//极点排序 GetHull(); printf("%.2lf\n",GetMaxPtPairDis()); } return 0; }