【題解】「UVA11626」Convex Hull
凸包模板題。
之前寫過拿 Graham 演算法求凸包的,為了不重複/多學點知識,那這次拿 Andrew 演算法求凸包吧qaq
*此文章所有圖片均為作者手畫。
Andrew 演算法
假設我們有這些點:
首先把所有點以橫座標為第一關鍵字,縱座標為第二關鍵字排序。
相對於 Graham 演算法來說,Andrew 演算法排序更簡單,按 \(x, y\) 座標排序,時間複雜度也更低(一般的座標系中排序方法)。
首先將 \(p_1\) 入棧。
然後也將 \(p_2\) 入棧,\(p_2\) 可能在,也可能不在,等著之後判斷。
隨後,發現 \(p_3\) 偏右,所以我們將 \(p_2\) 出棧。
發現 \(p_4\)
\(p_5\) 向右,\(p_4\) 出棧,\(p_5\) 入棧。
\(p_6\) 向左,入棧。
\(p_7\) 向右,\(p_6\) 出棧,\(p_7\) 入棧。
\(p_8\) 向右,\(p_7\) 出棧,繼續檢查發現相對於 \(p_5\) \(p_8\) 仍然向右,\(p_5\) 出棧,\(p_8\) 入棧。
此時,我們發現,凸包明明還空一半就到頭了???
然而這是意料之中,我們這種演算法必然會只算出一半的凸包。
所以我們需要再從排序末尾的點(也就是 \(p_8\))出發,按照一模一樣的方式再算一遍就行了。
當然如果我們走過的點就不許要再走了(除了 \(p_1\)
從 \(p_8\) 到 \(p_7\),向左,\(p_7\) 入棧。
\(p_6\) 向右,\(p_7\) 出棧,\(p_6\) 入棧。
\(p_5\) 向左,入棧。
\(p_4\) 向左,入棧。
\(p_3\) 向右,\(p_4\) 出棧,對於 \(p_5\) \(p_3\) 依然向右,\(p_5\) 出棧,\(p_3\) 入棧。
\(p_2\) 向右,\(p_3\) 出棧,\(p_2\) 入棧。
最後將 \(p_2\) 和 \(p_1\) 連起來。
至此,我們的 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