凸包和平面最远点对

模板:洛谷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;
}
View Code

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;
}
View Code(暴力)

猜你喜欢

转载自www.cnblogs.com/sun123zxy/p/convexhull.html
今日推荐