Loading

【题解】Uva[UVA11626] Convex Hull

凸包模板题。

之前写过拿 Graham 算法求凸包的,为了不重复/多学点知识,那这次拿 Andrew 算法求凸包吧qaq

*此文章所有图片均为作者手画。


Andrew 算法

假设我们有这些点:

1.PNG

首先把所有点以横坐标为第一关键字,纵坐标为第二关键字排序。

2.PNG

相对于 Graham 算法来说,Andrew 算法排序更简单,按 \(x, y\) 坐标排序,时间复杂度也更低(一般的坐标系中排序方法)。

首先将 \(p_1\) 入栈。

然后也将 \(p_2\) 入栈,\(p_2\) 可能在,也可能不在,等着之后判断。

3.PNG

随后,发现 \(p_3\) 偏右,所以我们将 \(p_2\) 出栈。

4.PNG

发现 \(p_4\) 依然偏右,\(p_3\) 出栈,\(p_4\) 入栈。

5.PNG

\(p_5\) 向右,\(p_4\) 出栈,\(p_5\) 入栈。

6.PNG

\(p_6\) 向左,入栈。

7.PNG

\(p_7\) 向右,\(p_6\) 出栈,\(p_7\) 入栈。

8.PNG

\(p_8\) 向右,\(p_7\) 出栈,继续检查发现相对于 \(p_5\) \(p_8\) 仍然向右,\(p_5\) 出栈,\(p_8\) 入栈。

9.PNG

此时,我们发现,凸包明明还空一半就到头了???

然而这是意料之中,我们这种算法必然会只算出一半的凸包。

所以我们需要再从排序末尾的点(也就是 \(p_8\))出发,按照一模一样的方式再算一遍就行了。

当然如果我们走过的点就不许要再走了(除了 \(p_1\)

\(p_8\)\(p_7\),向左,\(p_7\) 入栈。

10.PNG

\(p_6\) 向右,\(p_7\) 出栈,\(p_6\) 入栈。

11.PNG

\(p_5\) 向左,入栈。

12.PNG

\(p_4\) 向左,入栈。

13.PNG

\(p_3\) 向右,\(p_4\) 出栈,对于 \(p_5\) \(p_3\) 依然向右,\(p_5\) 出栈,\(p_3\) 入栈。

14.PNG

\(p_2\) 向右,\(p_3\) 出栈,\(p_2\) 入栈。

15.PNG

最后将 \(p_2\)\(p_1\) 连起来。

16.PNG

至此,我们的 Andrew 算法就完成了!

扫描的时间复杂度:\(O(n)\)(已过滤常数)

排序时间复杂度:\(O(n \log n)\)

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


Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#define line cout << endl
using namespace std;
const int NR = 1e5 + 5;
const double eps = 1e-7;
int n;
struct point {
    double x, y;
    point () {}
    point (double a, double b) : x (a), y (b) {}
    bool operator < (const point &b) const {
        if (x < b.x) return 1;
        if (x > b.x) return 0;
        return y < b.y;
    }
    point operator - (const point &b) {
        return point (x - b.x, y - b.y);
    }
};
point p[NR], sp[NR];
int cmp (double x) {
    if (fabs (x) < eps) return 0;
    return x > 0 ? 1 : -1;
}
double dis (point a, point b) {
    return sqrt ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double cp (point a, point b) {
    return a.x * b.y - a.y * b.x;
}
int Andrew () {
    sort (p + 1, p + 1 + n);
    int len = 0;
    for (int i = 1; i <= n; i++) {
        while (len > 1 && cmp (cp (sp[len] - sp[len - 1], p[i] - sp[len - 1])) < 0) 
            len--;
        sp[++len] = p[i];
    }
    int k = len;
    for (int i = n - 1; i >= 1; i--) {
        while (len > k && cmp (cp (sp[len] - sp[len - 1], p[i] - sp[len - 1])) < 0)
            len--;
        sp[++len] = p[i];
    }
    return len;
}
int main () {
    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        char c;
        for (int i = 1; i <= n; i++)
            cin >> p[i].x >> p[i].y >> c;
        int t = Andrew();
        cout << t - 1 << endl;
        for (int i = 1; i < t; i++) 
            printf ("%.0lf %.0lf\n", sp[i].x, sp[i].y);
    }
    return 0;
}

谢谢qaq

posted @ 2020-07-22 19:42  AgrumeStly  阅读(232)  评论(0编辑  收藏  举报