• AcWing算法进阶课 计算几何 凸包


    凸包

    什么是凸包

    假设平面上现在有若干个点,现在我们过某些点做一个多边形,是这个多边形能够将所有点都包含起来【在多边形边上也算】。当这个多边形是一个凸多边形的时候,我们称它问凸包。

    可以想象为给这些点外面箍上一个橡皮筋。

    解决凸包问题的算法

    暴力

    时间复杂度为O(n^3)

    思路:先确定两个点,然后枚举剩下的其他所有点,如果其他所有点都在这条直线的同一侧,则这两个点是凸包上的点,否则就不是。

    解决:

    1.枚举点对(将所有的点两两配对)组成的直线,对于每条直线,检查是否剩下的(n-2)个点是否在直线的同侧。

    假设枚举的点对是a和b,而新的点是c,那么我们只需要判断他们的叉积是否同时为正或者同时为负就OK了。

     

    Andrew算法

    时间复杂度O(nlogn)【时间主要是在对坐标的快排上】

    Andrew算法:Andrew算法是对Graham扫描法的改进。-

    原理:

    利用夹角,让整个图形保持左转。先将最左边的前两个点加入栈中,每次加入新点时判断是否左拐(叉积大于0),如果是就将新点直接加入;如果不是,就弹出栈顶,直到左拐,将新点加入栈中。【注意,栈中要保证至少有一个元素,也就是top>=2的时候才可以弹出栈顶】第一遍找的都是y小的点,也就是下凸包,上面没有,然后我们反着再来一遍。

    流程:

    1.将所有点进行快排,以x为第一关键字,y为第二关键字升序排序

    2.先从左至右维护凸包的下半部分,然后再从右至左谓语上半部分。

    3.将第一个点放入栈中,【这个点一定时凸包的最左边的点了,是不会清理掉的】,然后在将第二个点放入栈中。当栈中元素大于等于2的时候,就要判断栈顶元素是否还要保留。

    如果新点在栈顶元素和次栈顶元素所组成的直线的右侧,那么,直接将新点加入栈中。

    如果新点在栈顶元素和次栈顶元素所组成的直线的左侧,那么,将栈顶元素不断弹出,直到新点的位置出现在栈顶元素与次栈顶元素所在直线的右侧结束。

    那么,我们这个过程,是从左往右走的,而且每次找的点都是在当前直线的右侧,也就是直线的下方向,那么我们得到的凸包就是我们的下半部分。

    求上半部分的时候,从右往左排就自然而然是对的了。

     

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 10005;
    struct Convex_hell{
     struct Point{
       double x, y;
       //重载小于号,用于sort排序
       bool operator< (const Point &W) const{
         if (x == W.x) return y < W.y;
         return x < W.x;
      }
       //重载减号,用于计算向量
       Point operator- (const Point &W) const{
         return {x - W.x, y - W.y};
      }
    };
     int n;
     Point q[N];
     int stk[N], top = 0;
     bool used[N];
     //获得两点之间的距离
     double get_dist(Point a, Point b)
    {
       double dx = a.x - b.x;
       double dy = a.y - b.y;
       return sqrt(dx * dx + dy * dy);
    }
     //获得向量叉积
     double cross(Point a, Point b)
    {
       return a.x * b.y - a.y * b.x;
    }
     //将三个点转为向量ab,和ac,计算向量叉积,如果是正说明点c在向量的左侧,是求凸包的方向。
     double area(Point a, Point b, Point c)
    {
       return cross(b - a, c - a);
    }
     //andrew算法,求凸包
     double andrew()
    {
       sort(q, q + n);
       for (int i = 0; i < n; i ++ )
      {
         while (top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0)
        {
           if (area(q[stk[top - 1]], q[stk[top]], q[i]) < 0)
             used[stk[top -- ]] = false;
           else top --;
        }
         stk[++ top] = i;
         // for (int i = 1; i <= top; i ++ ) cout << q[stk[i]].x << " " << q[stk[i]].y << endl;
         // printf("--------------------- ");
         used[i] = true;
      }
       // printf("<---------I am the happiness divider---------> ");
       used[0] = false;
       for (int i = n - 1; i >= 0; i -- )
      {
         if (used[i]) continue;
         while (top >= 2 && area(q[stk[top - 1]], q[stk[top]], q[i]) <= 0)
           top --;//因为不需要used标记了,所以才显得这么短
         stk[++ top] = i;
         // for (int i = 1; i <= top; i ++ ) cout << q[stk[i]].x << " " << q[stk[i]].y << endl;
         // printf("--------------------- ");
      }
       double res = 0;
       for (int i = 2; i <= top; i ++ )
         res += get_dist(q[stk[i - 1]], q[stk[i]]);
       return res;
    }
    }O;
    int main()
    {
     cin >> O.n;
     for (int i = 0; i < O.n; i ++ ) scanf("%lf%lf", &O.q[i].x, &O.q[i].y);
     printf("%.2lf ", O.andrew());
     return 0;
    }

     

  • 相关阅读:
    python调用函数
    python递归函数的执行过程
    linux rwx 权限说明
    linux ssh scp免密码
    linux的bash特性
    python3常用的内置函数
    linux清理系统缓存
    vim常用命令
    公司项目安装禅道
    jquery 自定义动画
  • 原文地址:https://www.cnblogs.com/Iamcookieandyou/p/15012636.html
Copyright © 2020-2023  润新知