15.8 计算几何(凸包)——【求土包子】

题目描述

平面直角坐标系中求一个凸包的周长C。

输入描述

在这里插入图片描述

输出描述

输出C,结果保留6位小数。

输入输出样例

输入:

4
4 8
4 12
5 9.3
7 8

输出:

12.000000



最终代码c/c++

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5;

const double eps = 1e-8;
int sgn(double x){        //判断x是否等于0
    if(fabs(x) < eps)  return 0;
    else return x<0?-1:1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double x, double y):x(x),y(y){}
    Point operator + (Point B){return Point(x+B.x,y+B.y);}
    Point operator - (Point B){return Point(x-B.x,y-B.y);}
    bool operator == (Point B){return sgn(x-B.x) == 0 && sgn(y-B.y) == 0;}
    bool operator < (Point B){                  //用于sort()排序
        return sgn(x-B.x)<0 || (sgn(x-B.x)==0 && sgn(y-B.y)<0);}
};
Point p[N],ch[N];    //输入点是p[],凸包顶点放在ch[]中
typedef Point Vector;
double Cross(Vector A,Vector B){return A.x*B.y - A.y*B.x;} //叉积
double Distance(Point A,Point B){return hypot(A.x-B.x,A.y-B.y);}


//求凸包。凸包顶点放在ch中,返回值是凸包的顶点数
int Convex_hull(Point *p,int n,Point *ch)
{
    sort(p,p+n);          //对点排序:按x从小到大排序,如果x相同,按y排序
    n=unique(p,p+n)-p;   //去除重复点
    int v=0;
    //求下凸包。如果p[i]是右拐弯的,这个点不在凸包上,往回退
    for(int i=0;i<n;i++)
    {
        while(v>1 && sgn(Cross(ch[v-1]-ch[v-2],p[i]-ch[v-2]))<=0)
            v--;

        ch[v++]=p[i];
    }
    int j=v;
    //求上凸包
    for(int i=n-2;i>=0;i--)
    {
        while(v>j && sgn(Cross(ch[v-1]-ch[v-2],p[i]-ch[v-2]))<=0)
            v--;
        ch[v++]=p[i];
    }


    if(n>1) v--;
    return v;                   //返回值v是凸包的顶点数
}
int main(){
    int n;
    cin >>n;
    for(int i=0;i<n;i++)
        scanf("%lf%lf",&p[i].x,&p[i].y);
    int v = Convex_hull(p,n,ch);    //返回凸包的顶点数v


    double ans=0;
    if(v==1) ans=0;
    else if(v==2) ans=Distance(ch[0],ch[1]);
    else
         for(int i=0;i<v;i++)          //计算凸包周长
              ans+=Distance(ch[i],ch[(i+1)%v]);

    printf("%.6f\n",ans);
    return 0;
}



凸包

凸包(Convex hull)可是计算几何中的著名问题:
给定一些点,求能把所有这些点包含在内的面积最小的多边形。可以想象有一个很大的橡皮箍,它把所有的点都箍在里面,橡皮箍收紧之后,绕着最外围的点形成的多边形就是凸包。

凸包算法的基本思路是:
旋转扫除,即设定一个参照顶点,逐个旋转到其它所有顶点,并判断这些顶点是否在凸包上。
算法做两次扫描,
先从最左边的点沿“下凸包”扫描到最右边,
再从最右边的点沿“上凸包”扫描到最左边,
“上凸包”和“下凸包”合起来就是完整的凸包。复杂度是O(nlogn)。


求凸包的具体步骤可以分为三步:

  1. 把所有点按照横坐标x从小到大进行排序,如果相同,按y从小到大排序。并删除重复的点,得到序列 { p0,p1,p2,…,pm }
  2. 从左到右扫描所有点,求“下凸包”。 p0一定在凸包上,它是凸包的最左边的顶点,从p0开始,依次检查{ p0,p1,p2,…,pm },扩展出“下凸包”。
    判断的依据是:
    如果新点在凸包“前进"方向的左边,说明在“下凸包”上,把它加入到凸包;如果在右边,说明拐弯了,删除最近加入下凸包的点。继续这个过程,直到检查完所有点。
    拐弯方向用叉积判断即可
    例如下图,检查p4时,发现p4,p3对p3,p2是右拐弯的,说明p3不在下凸包上(有可能在“上凸包”上,在步骤3中会判断);退回到p2,继续发现p4,p2对p2,p1也是右拐弯的,退回到p1
    在这里插入图片描述
  3. 从右到左重新扫描所有点,求“上凸包”。和求“下凸包"过程类似,最右边的点pm一定在凸包上。



对于复杂度:
算法先对点排序,复杂度是O(nlogn),然后扫描O(n)次得到凸包。所以算法的总复杂度是O(nlogn)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你说的白是什么白_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值