凸包学习记录

学习记录

兴起捡起很久以前就研究过的凸包。
还是没有在学校共享里找到资料……
这里写图片描述
没有办法,只能又推荐dalao的博客了……
配合板题食用更佳!
P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows

进入正题

凸包可广泛运用于各种求最小点覆盖问题,方法千奇百怪,这里只列举三种。
涉及到向量的应用。

给出 n n 个点的坐标,求最短的能够围住这些点的多边形的周长。
引用一张经典的图:
这里写图片描述

法一:暴力枚举法

暴力出奇迹,水法真神奇

基本思想

枚举两点,判断其余点是否在这两点所在直线的一侧:
若是,则这两点为凸包上的点;
若不是,则继续枚举。

法二:Graham扫描法

这里写图片描述

基本思想

  1. y坐标最小的点为原点,建系

    • 将点按照极角大小排序
    • 将当前点与栈顶边比较:若当前点在栈顶边的右侧,弹出栈顶点,重复当前步骤,否则进行下一步
    • 若当前点在栈顶边的左侧,将当前点加入栈,更换当前点
    • 时间复杂度: O(nlogn) O ( n log ⁡ n )

      法三:Melkman算法

      Melkman算法是当前世界上公认的最优秀求凸包算法。
      已知上述两种算法都是离线算法,而Melkman算法是在线算法。
      时间复杂度: O(n) O ( n ) (显然优于目前其他任何算法)
      经过反复斟酌,这位dalao的博客是比较详细且优秀的,这里不再赘述。
      (你就是想掩饰你不会)

      具体用途与实现

      P2742 【模板】二维凸包 / [USACO5.1]圈奶牛Fencing the Cows

      Code

      //不知道CSDN的Markdown是不是萎了,颜色渲染有点问题,凑合着看吧,也不知道什么时候修。
      #include <cstdio>
      #include <cstring>
      #include <cmath>
      #include <algorithm>
      using namespace std;
      
      struct Dot
      {
          double x, y;
          int pos;
          Dot(double _x = 0, double _y = 0)
          {
              x = _x, y = _y;
          }
      };
      
      struct Line
      {
          Dot p, v;
          Line() {}
          Line(Dot _p, Dot _v)
          {
              p = _p, v = _v;
          }
      };
      
      Dot operator+(Dot a, Dot b) {return Dot(a.x + b.x, a.y + b.y);}
      Dot operator-(Dot a, Dot b) {return Dot(a.x - b.x, a.y - b.y);}
      Dot operator*(Dot a, double b) {return Dot(a.x * b, a.y * b);}
      double DotTimes(Dot a, Dot b) {return a.x * b.x + a.y * b.y;}
      double CrossTimes(Dot a, Dot b) {return a.x * b.y - a.y * b.x;}
      double Len(Dot a) {return sqrt(DotTimes(a, a));}
      double Dist(Dot a, Dot b) {return Len(a - b);}
      bool Direct(Dot a, Dot b) {return CrossTimes(a, b) < 0;}
      
      const int MAXN = 10000;
      const int Maxint = 2147483647;
      
      int n;
      Dot src = Dot(0, Maxint);
      Dot d[MAXN + 1];
      int q[MAXN + 1];
      double ans;
      
      bool cmp(Dot a, Dot b)
      {
          double tmp = CrossTimes(a, b);
      
          if (tmp == 0)
              return Len(a) < Len(b);
          else
              return tmp > 0;
      }
      
      int main(int argc, char const *argv[])
      {
          //freopen("init.in", "r", stdin);
          scanf("%d", &n);
      
          for (int i = 1; i <= n; i++)
          {
              scanf("%lf%lf", &d[i].x, &d[i].y);
              d[i].pos = i;
      
              if (d[i].y < src.y)
                  src = d[i];
          }
      
          for (int i = 1; i <= n; i++)
          {
              int tmp = d[i].pos;
              d[i] = d[i] - src;
              d[i].pos = tmp;
          }
      
          sort(d + 1, d + n + 1, cmp);
      
          for (int i = 1; i <= n; i++)
          {
              while ((Direct(d[q[q[0]]] - d[q[q[0] - 1]], d[i] - d[q[q[0]]])) && (q[0] > 2))
                  q[0]--;
      
              q[++q[0]] = i;
          }
      
          q[q[0] + 1] = q[1];
      
          for (int i = 1; i <= q[0]; i++)
              ans += Len(d[q[i + 1]] - d[q[i]]);
      
          printf("%.2lf\n", ans);
          return 0;
      }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值