zpy12345 | 个人主页

  • Hero
  • 获奖信息
  • 作品展示
  • 博客
  • 联系方式
  • …  
    • Hero
    • 获奖信息
    • 作品展示
    • 博客
    • 联系方式

    zpy12345 | 个人主页

    • Hero
    • 获奖信息
    • 作品展示
    • 博客
    • 联系方式
    • …  
      • Hero
      • 获奖信息
      • 作品展示
      • 博客
      • 联系方式

      二维凸包——Andrew 算法学习笔记

      Andrew 算法学习笔记

      · 算法学习笔记

      # 例题:[P2742【模板】二维凸包](https://www.luogu.com.cn/problem/P2742)

      更适合初学者体质的题解。

      ## 1.凸包介绍

      在平面上能包含所有给定点的**最小凸多边形**叫做凸包。

      根据三角不等式,凸包也一定是包含所有给定点的**周长最小多边形**。

      同样可以理解为,我们在地上钉了一些钉子,有一个有弹性皮筋,我们把它拉大框住所有钉子,然后松手,皮筋的形状就是凸包。

      上凸壳(qiao or ke?)一般指凸包的上半部分,看完本篇题解应该会有更深刻的理解。

      显然,这个题目求的是凸包的周长。

      ## 2.Andrew 算法介绍

      一个求凸包的算法,或许也叫安德鲁算法?

      我们先按照横坐标为第一关键字,纵坐标为第二关键字,从小到大排序。显然,第一个点和最后一个点一定在凸包上。

      我们先考虑求上凸壳,下凸壳同理。

      在排好序的点中从小到大枚举,加入一个新点 $c$ 时,如果形成了凹多边形(如下图),那么不合法,依照三角不等式,连接点 $a$ 和点 $c$ 更优,我们直接删掉点 $b$,连接点 $a$ 和点 $c$。否则直接加入点 $c$,连接点 $b$ 和点 $c$。

      ![](https://cdn.luogu.com.cn/upload/image_hosting/2hf84gmg.png)

      ![](https://cdn.luogu.com.cn/upload/image_hosting/xuwvfa6x.png)

      这样就可以求出上凸壳了。

      实现过程其实就是单调栈。

      其实按照双关键字排序的作用只有求出第一个和最后一个点,在后续算法中,只需要按照横坐标排序即可。同样,我们发现,除了最后一个点之外,其余相同横坐标的情况下,只有纵坐标最高的点有可能成为上凸壳上的点。当然,这里直接用排序好的数组即可,方便,并且相同横坐标下纵坐标低的也不会影响结果。

      如何判断是否形成凹多边形?可以发现,直线 $ab$ 的斜率小于直线 $bc$ 的斜率,据此判断即可。如果求斜率有精度问题,可以去分母求(详见代码)。这里别人用的是向量,不过考虑初学者能力,先介绍斜率。

      这样,我们就得到了上凸壳的一个性质:其斜率依次递减。

      时间复杂度 $O(n\log_{2}{n}) $,瓶颈在于排序。

      ## 3.代码

      ```cpp

      #include

      //#define printf __mingw_printf

      //在一些本地编译器(如DEV和小熊猫)中,long double有兼容性问题,需要用

      //__mingw_printf输出,不过洛谷不需要,而且用__mingw_printf会报错

      using namespace std;

      const int N=1e5+5;

      struct point

      {

      double x,y;

      }a[N],q[N];

      int n,tp;

      bool cmp(point a,point b)

      {

      return a.x==b.x?a.y

      }

      int main()

      {

      scanf("%d",&n);

      for(int i=1;i<=n;i++)

      scanf("%lf%lf",&a[i].x,&a[i].y);

      sort(a+1,a+1+n,cmp);

      q[++tp]=a[1];

      for(int i=2;i<=n;i++)//求上凸壳

      {

      while(tp>=2&&(a[i].y-q[tp].y)*(q[tp].x-q[tp-1].x)>=(q[tp].y-q[tp-1].y)*(a[i].x-q[tp].x))

      tp--;

      q[++tp]=a[i];

      }

      long double ans=0;//以防万一爆掉

      for(int i=1;i

      ans+=sqrt((q[i].y-q[i+1].y)*(q[i].y-q[i+1].y)+(q[i].x-q[i+1].x)*(q[i].x-q[i+1].x));

      tp=1,q[tp]=a[n];

      for(int i=n-1;i>=1;i--)

      {

      while(tp>=2&&(a[i].y-q[tp].y)*(q[tp].x-q[tp-1].x)>=(q[tp].y-q[tp-1].y)*(a[i].x-q[tp].x))

      tp--;

      q[++tp]=a[i];

      }

      for(int i=1;i

      ans+=sqrt((q[i].y-q[i+1].y)*(q[i].y-q[i+1].y)+(q[i].x-q[i+1].x)*(q[i].x-q[i+1].x));

      printf("%.2Lf",ans); //用Lf输出

      return 0;

      }

      ```

      ## 4.扩展用途

      有什么用呢?有没有发现凸包和斜率优化很像?当然还有更多用途,这里就不多说了。

      订阅
      上一篇
      CSP-S2025初赛游记
      下一篇
      2026联合省选游记
       回到主页
      feed iconstrikingly icon上线了提供技术支持
      Cookie的使用
      我们使用cookie来改善浏览体验、保证安全性和数据收集。一旦点击接受,就表示你接受这些用于广告和分析的cookie。你可以随时更改你的cookie设置。 了解更多
      全部接受
      设置
      全部拒绝
      Cookie设置
      必要的Cookies
      这些cookies支持诸如安全性、网络管理和可访问性等核心功能。这些cookies无法关闭。
      分析性Cookies
      这些cookies帮助我们更好地了解访问者与我们网站的互动情况,并帮助我们发现错误。
      首选项Cookies
      这些cookies允许网站记住你的选择,以提供更好的功能和个性化支持。
      保存