題目: UVa - 1206 - Boundary Points

題目說明

給一些在二維平面的座標點,求包住所有座標點的最小多邊形的頂點座標。

Input: 每行為一組測資,每組括號為一個座標點。

Output: 依照 Input 的格式輸出那些點的座標,起點在最後必須再輸出一次。

解題思路

此解法需要有 Convex Hull 及 Graham Scan 演算法的概念

簡單的凸包題,使用 Graham Scan 演算法找出所有頂點即可。

參考解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <bits/stdc++.h>

using namespace std;

#define FOR(i, a, b) for (int i = a; i < b; ++i)
#define CLR(c) memset(c, 0, sizeof(c))

static auto __ = []
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
return 0;
}();

const int INF = (int)1e9;
const int MXN = 100000;

struct Point
{
double x;
double y;
double d; // 與第一個點的距離
};

Point P[MXN];

string str;
int N;
int top; // 凸包頂點數

// 兩點間的距離
double dist(const Point& l, const Point& r)
{
return sqrt(pow(l.x - r.x, 2) + pow(l.y - r.y, 2));
}

double crossProduct(const Point& o, const Point& a, const Point& b)
{
return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y);
}

// 選出最下面的點,若有相同 y 的點則選擇 x 較小的那個
bool cmp1(const Point& l, const Point& r)
{
return l.y == r.y ? l.x < r.x : l.y < r.y;
}

// 極角排序,cp > 0: ol -> or 逆時針
bool cmp2(const Point& l, const Point& r)
{
auto cp = crossProduct(P[0], l, r);
return cp == 0 ? l.d < r.d : cp > 0;
}

void read()
{
N = 0;
char c;
stringstream ss(str);
while (ss >> c >> P[N].x >> c >> P[N].y >> c) ++N;
}

// Convex Hull
void GrahamScan()
{
// 找出第一個點,並將其他點依照極角排序
sort(P, P + N, cmp1);
FOR(i, 1, N) P[i].d = dist(P[0], P[i]);
sort(P + 1, P + N, cmp2);

// 找凸包
top = 1;
P[N++] = P[0];
FOR(i, 2, N)
{
while (top > 0 && crossProduct(P[top - 1], P[top], P[i]) <= 0) --top;
P[++top] = P[i];
}
}

void solve()
{
FOR(i, 0, top + 1)
{
if (i) cout << ' ';
cout << '(' << P[i].x << ',' << P[i].y << ')';
}
cout << '\n';
}

int main()
{
while (getline(cin, str))
{
read();
GrahamScan();
solve();
}
}