凸包+旋转卡壳法

凸包+旋转卡壳法
题目:poj2187
寻找最远点对距离

#include<bits/stdc++.h>
using namespace std;
int n;
double eps=1e-10;
double add(double a,double b){//考虑精度的求和
    if(abs(a+b)<eps*(abs(a)+abs(b))) return 0;//小于精度就是0
    return a+b;//否则正常加,其实就多了上面一句,对个和要有精度判断
}
struct p{
    double x,y; //坐标
    p(){}       //空构造器
    p(double x,double y):x(x),y(y){}//构造器重载
    p operator + (p pp){return p(add(x,pp.x),add(y,pp.y));}     //+
    p operator - (p pp){return p(add(x,-pp.x),add(y,-pp.y));}   //-
    p operator * (double d){return p(x*d,y*d);}     //线乘
    double dot(p pp){return add(x*pp.x,y*pp.y);}    //叉乘
    double det(p pp){return add(x*pp.y,-y*pp.x);}   //点乘
};
p ps[100];//开一个点数组
bool cmp_x(const p& pp,const p&qq){//比较函数
    if(pp.x!=qq.x)return pp.x<qq.x;//X坐标小的优先
    return pp.y<qq.y;//然后就是Y小的优先
}
vector<p> convex_hull(p* ps,int n){//求凸包,传入数组及数量,传出向量
    sort(ps,ps+n,cmp_x);//先由左到右排序,同X则由下到上
    int k=0;//K个点初始化为0
    vector<p> qs(n*2);//这就是处理后的数组,先开两倍,因为Kl扫两轮可能会
    for(int i=0;i<n;i++){//扫一次N个点得到下半轮廓
        while(k>1&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0) k--;//
        qs[k++]=ps[i];//压入去
    }
    for(int i=n-2,t=k;i>=0;i--){//再扫回来得到上半轮廓
        while(k>t&&(qs[k-1]-qs[k-2]).det(ps[i]-qs[k-1])<=0) k--;
        qs[k++]=ps[i];//压入去
    }               //注意最后K++会多了一
    qs.resize(k-1); //所以把长度缩一就是最终的长度了
    return qs;
}
double dist(p pp,p qq){         //求距离
    return (pp-qq).dot(pp-qq);  //其实这里是方和
}
void solve(){
    cin>>n;//输入N
    for(int i=0;i<n;i++)cin>>ps[i].x>>ps[i].y;  //输入N个点
    vector<p> qs=convex_hull(ps,n);//执行凸包,删去内部点
    int n=qs.size();    //得到新的凸包点集的点数
    if(n==2){           //只果只有两个点
        printf("%.0f\n",dist(qs[0],qs[1]));     //输出
        return ;
    }
    int i=0,j=0;                    //开始旋转卡壳法
    for(int k=0;k<n;k++{            //先把全部点扫一次
        if(!cmp_x(qs[i],qs[k]))i=k; //I是横坐标最右的点,有多个则上者
        if(cmp_x(qs[j],qs[k]))j=k;  //J是横坐标最左的点,有多个则取下者
    }
    double res=0;//初始化答案
    int si=i,sj=j;//初始化两个扫描点
    while(i!=sj||j!=si){//直到两个扫描点互换位置(差不多就是都扫了180度的样子)
        res=max(res,dist(qs[i],qs[j]));
        if((qs[(i+1)%n]-qs[i]).det(qs[(j+1)%n]-qs[j])<0)//叉乘判断谁哪个旋转点旋转
            i=(i+1)%n;  //I移到下一个点
        else j=(j+1)%n; //J移到下一个点
    }//上面的判断要用数学方法解释:叉乘的值与旋转角度的关系
    printf("%.0f\n",res);//输出答案
}
int main(){
    solve();
    return 0;
}
/*
Input	output
8		80
0 5
1 8
3 4
5 0
6 2
6 6
8 3
8 7
补充:关于空间中求点距
两点间距离有三种:欧几/曼哈/MAX(X,Y)距离
询问可以是求空间中一点,令其到所有点的距离最大值最小,或距离的和最小
思路:三分套三分,三维套三重

向量
A X B 大于0 A在B顺时针方向
A X B 小于0 A在B逆时针方向
A X B 等于0 AB共线方向不定

#define eps 1e-8
int sgn(double x)
{
    if(fabs(x)<eps)return 0;
    if(x<0)return -1;
    else return 1;
}


*/

猜你喜欢

转载自blog.csdn.net/cj1064789374/article/details/84932967