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
| // 더 많은 정보는 42jerrykim.github.io 에서 확인하세요.
#include <bits/stdc++.h>
using namespace std;
using int64 = long long;
using Point = pair<int64, int64>;
static int dp[2][3030][3030];
static int prvIdx[2][3030][3030];
inline int ccw(const Point& a, const Point& b, const Point& c) {
int64 v = (b.first - a.first) * (c.second - a.second)
- (b.second - a.second) * (c.first - a.first);
if (v > 0) return 1;
if (v < 0) return -1;
return 0;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<Point> pts(n);
for (int i = 0; i < n; i++) cin >> pts[i].first >> pts[i].second;
// Find heel: unique lowest y
int heel = 0;
for (int i = 1; i < n; i++) {
if (pts[i].second < pts[heel].second) heel = i;
}
swap(pts[0], pts[heel]);
// Sort by angle around heel (counter-clockwise)
sort(pts.begin() + 1, pts.end(), [&](const Point& a, const Point& b) {
return ccw(pts[0], a, b) > 0;
});
// DP transitions optimized with angular order + sliding window
for (int i = 1; i < n - 1; i++) {
vector<int> a(1), b; // a[0] = 0 (heel) as base
for (int j = 1; j < i; j++) {
if (ccw(pts[0], pts[j], pts[i]) != 0) a.push_back(j);
}
for (int j = i + 1; j < n; j++) {
if (ccw(pts[0], pts[i], pts[j]) != 0) b.push_back(j);
}
sort(a.begin(), a.end(), [&](int u, int v) {
return ccw(pts[i], pts[u], pts[v]) > 0;
});
sort(b.begin(), b.end(), [&](int u, int v) {
return ccw(pts[i], pts[u], pts[v]) > 0;
});
// dp[1][i][j] from dp[0][k][i] while angle(k,i) < angle(j,i) around i
int pv = 0, best = 0, bestIdx = 0;
for (int j : b) {
while (pv < (int)a.size() && ccw(pts[a[pv]], pts[i], pts[j]) > 0) {
int k = a[pv++];
if (best < dp[0][k][i] + 1) {
best = dp[0][k][i] + 1;
bestIdx = k;
}
}
dp[1][i][j] = best;
prvIdx[1][i][j] = bestIdx;
}
// Reverse sweep for dp[0][i][j] from dp[1][k][i] with opposite inequality
pv = 0; best = 0; bestIdx = 0;
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
for (int j : b) {
while (pv < (int)a.size() && ccw(pts[a[pv]], pts[i], pts[j]) < 0) {
int k = a[pv++];
if (best < dp[1][k][i] + 1) {
best = dp[1][k][i] + 1;
bestIdx = k;
}
}
dp[0][i][j] = best;
prvIdx[0][i][j] = bestIdx;
}
}
// Find best end (x -> y) respecting left turn at i around heel
int best = 0, x = 0, y = 0;
for (int i = 1; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (ccw(pts[0], pts[i], pts[j]) != 1) continue;
if (best < dp[0][i][j]) {
best = dp[0][i][j];
x = i; y = j;
}
}
}
if (best == 0) {
cout << 0;
return 0;
}
// Reconstruct sequence of vertex indices starting from heel (0)
vector<int> path;
path.push_back(y);
path.push_back(x);
while (path.back() != 0) {
int t = (int)path.size();
int prev = prvIdx[t & 1][path[t - 1]][path[t - 2]];
path.push_back(prev);
}
reverse(path.begin(), path.end());
cout << (int)path.size() << '\n';
for (int idx : path) {
cout << pts[idx].first << ' ' << pts[idx].second << '\n';
}
return 0;
}
|