凸包学习记录

学习记录

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

进入正题

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

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

法一:暴力枚举法

暴力出奇迹,水法真神奇

基本思想

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

法二:Graham扫描法

这里写图片描述

基本思想

  1. y 坐标最小的点为原点,建系
  2. 将点按照极角大小排序
  3. 将当前点与栈顶边比较:若当前点在栈顶边的右侧,弹出栈顶点,重复当前步骤,否则进行下一步
  4. 若当前点在栈顶边的左侧,将当前点加入栈,更换当前点

时间复杂度: O ( n log n )

法三:Melkman算法

Melkman算法是当前世界上公认的最优秀求凸包算法。
已知上述两种算法都是离线算法,而Melkman算法是在线算法。
时间复杂度: 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;
}

猜你喜欢

转载自blog.csdn.net/ace_killing/article/details/80820641
今日推荐