-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsol.cpp
More file actions
executable file
·191 lines (168 loc) · 5.41 KB
/
Copy pathsol.cpp
File metadata and controls
executable file
·191 lines (168 loc) · 5.41 KB
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include <iostream>
#include <vector>
#include <set>
using namespace std;
struct Pt
{
/* Point/vector in 2D space */
double x, y;
Pt(double x, double y) : x(x), y(y) {}
friend bool operator<(const Pt &a, const Pt &b)
{
return (a.x < b.x) || ((a.x == b.x) && (a.y < b.y));
}
friend bool operator==(const Pt &a, const Pt &b)
{
return (a.x == b.x) && (a.y == b.y);
}
friend Pt operator+(const Pt &a, const Pt &b)
{
return Pt(a.x + b.x, a.y + b.y);
}
friend Pt operator-(const Pt &a, const Pt &b)
{
return Pt(a.x - b.x, a.y - b.y);
}
friend Pt operator*(const Pt &a, const double b)
{
return Pt(a.x * b, a.y * b);
}
friend double operator*(const Pt &a, const Pt &b)
{
/* Dot product, result is zero when the two vectors are
* perpendicular, or one of them is (0,0) */
return a.x * b.x + a.y * b.y;
}
double cross_product(const Pt &b) const
{
/* Cross product of two vectors, equal to the (signed)
* area of the parallelogram spanned by the vectors,
* positive if b points counter clockwise of this vector
*/
return (this->x * b.y) - (this->y * b.x);
}
};
double ccw(const Pt &A, const Pt &B, const Pt &O)
{
/* Compares the vectors OA and OB, returns positive if OB
points counter-clockwise from OA, negative if OB points
clockwise from OA, zero if co-linear. The returned
value is twice the area of the triangle OAB, positive
if OAB is counter-clockwise, negative if clockwise.*/
return (A - O).cross_product(B - O);
}
struct Poly
{
/* Polygon (not necessarily convex) */
vector<Pt> points; /* Vertices of the polygon in
counter-clockwise order */
template <class InputIterator>
Poly(InputIterator first, InputIterator last)
: points(first, last){};
double area()
/* Returns the area of the polygon $O(|points|)$ */
{
double area = 0.0;
for (size_t i = 0; i < points.size(); i++)
{
area += points[i].cross_product(
points[(i + 1) % points.size()]);
}
return abs(area) / 2.0;
}
static Poly convex_hull(const vector<Pt> &points)
{
/* Returns the convex hull of a list of points.
* Uses Andrew's monotone chain algorithm.
* $O(|apoints| \log |apoints|)$*/
vector<Pt> lh, uh, hull;
// Sort points lexicographically, and remove duplicates
const set<Pt> S(points.begin(), points.end());
if (S.size() <= 1)
return Poly(S.begin(), S.end());
/* Build lower hull (lh) and upper hull (uh) */
for (const auto &p : S)
{
while ((lh.size() >= 2) &&
(ccw(lh.back(), p, lh.end()[-2]) <= 0))
lh.pop_back();
lh.push_back(p);
}
for (auto p = S.rbegin(); p != S.rend();
p++)
{
while ((uh.size() >= 2) &&
(ccw(uh.back(), *p, uh.end()[-2]) <= 0))
uh.pop_back();
uh.push_back(*p);
}
/* Concatenation of the lower and upper hulls gives the
* convex hull */
copy(lh.begin(), lh.end() - 1, back_inserter(hull));
copy(uh.begin(), uh.end() - 1, back_inserter(hull));
return Poly(hull.begin(), hull.end());
}
bool contains(const Pt &p) const
{
/* Returns whether a point is inside (or on the border
* of) the polygon. Uses the winding number method
* (works for non-convex polygons) $O(|points|)$. */
long long winding_nr = 0;
for (size_t i = 0; i < points.size(); i++)
{
const Pt &seg_start = points[i];
const Pt &seg_end =
points[(i + 1) % points.size()];
if (seg_start == p)
return true; /* On boundary as vertex*/
else if ((seg_start.y == p.y) &&
(seg_end.y == p.y))
{
/* Check if the point is on the boundary of
* the horizontal segment */
if ((min(seg_start.x, seg_end.x) <= p.x) &&
(p.x <= max(seg_start.x, seg_end.x)))
return true;
}
else
{
/* Update the winding number */
bool below = seg_start.y < p.y;
if (below != (seg_end.y < p.y))
{
double orientation =
ccw(p, seg_end, seg_start);
if (orientation == 0)
return true; // On boundary of a segment
if (below == (orientation > 0))
winding_nr += below ? 1 : -1;
}
}
}
return (winding_nr != 0);
}
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
int n;
while (true)
{
cin >> n;
if (n == 0)
break;
vector<Pt> points;
for (int i = 0; i < n; ++i)
{
Pt pt{0, 0};
cin >> pt.x >> pt.y;
points.push_back(pt);
}
Poly poly(points.begin(), points.end());
Poly hull = poly.convex_hull(points);
cout << hull.points.size() << '\n';
for (auto each : hull.points)
cout << each.x << " " << each.y << '\n';
}
}