2024 CCPC 哈尔滨 B 题 Concave Hull 题解
· 7 min read
题目
Concave Hull
2024 CCPC 哈尔滨 B 题
思路
-
凸包算法
- 使用 Andrew 算法分别求出外凸包
tu和内层点集的凸包nei - 标记外凸包上的点,剩余点为内层点
- 使用 Andrew 算法分别求出外凸包
-
旋转卡壳
- 枚举外凸包的每条边
(tu[i], tu[ni]) - 在内凸包上移动指针
j,找到距离当前边最近的点 - 维护最小距离
mi - 若不存在内层点,输出
-1
- 枚举外凸包的每条边
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using db = double;
#define dbg(x) cerr << #x << " = " << (x) << endl;
#define vdbg(a) cout << #a << " = "; for (auto x : a) cout << x << " "; cout << endl;
struct Point { ll x, y; };
using Vec = Point;
Vec operator-(Vec u, Vec v) {
return {u.x - v.x, u.y - v.y};
}
bool operator==(Vec u, Vec v) {
return u.x == v.x && u.y == v.y;
}
bool operator<(Vec u, Vec v) {
return u.x == v.x ? u.y < v.y : u.x < v.x;
}
ll cross(Vec u, Vec v) {
return u.x * v.y - u.y * v.x;
}
ll area2(vector<Point> &p) {
int n = p.size();
if (n < 3) return 0;
ll ans = 0;
for (int i = 0; i < n; ++i) {
int j = (i + 1) % n;
ans += cross(p[i], p[j]);
}
return abs(ans);
}
// Andrew 算法求凸包
vector<Point> Andrew(vector<Point> p) {
sort(p.begin(), p.end(), [&](Point a, Point b) {
return a.x == b.x ? a.y < b.y : a.x < b.x;
});
p.erase(unique(p.begin(), p.end()), p.end());
int n = p.size();
vector<Point> hi, lo;
for (int i = 0; i < n; ++i) {
while (hi.size() > 1 &&
cross(hi.back() - hi[hi.size() - 2], p[i] - hi.back()) >= 0) {
hi.pop_back();
}
while (lo.size() > 1 &&
cross(lo.back() - lo[lo.size() - 2], p[i] - lo.back()) <= 0) {
lo.pop_back();
}
hi.push_back(p[i]);
lo.push_back(p[i]);
}
vector<Point> hull;
for (int i = 0; i < lo.size(); ++i) hull.push_back(lo[i]);
for (int i = hi.size() - 2; i > 0; --i) hull.push_back(hi[i]);
return hull;
}
ll cal(Point a, Point b, Point c) {
return abs(cross(b - a, c - a));
}
void solve() {
int n; cin >> n;
vector<Point> p(n);
map<Point, int> mp;
for (int i = 0; i < n; ++i) {
ll x, y; cin >> x >> y;
p[i] = {x, y};
mp[p[i]] = i;
}
vector<bool> vis(n + 1);
vector<Point> tu = Andrew(p); // 外凸包
for (auto t : tu) vis[mp[t]] = 1;
vector<Point> t;
for (int i = 0; i < n; ++i) {
if (vis[i]) continue;
t.push_back(p[i]);
}
vector<Point> nei = Andrew(t); // 内层凸包
int j = 0, m1 = tu.size(), m2 = nei.size();
if (!m2) {
cout << "-1\n";
return;
}
ll mi = 4e18;
for (int i = 0; i < m1; ++i) {
int ni = (i + 1) % m1;
while (cal(tu[i], tu[ni], nei[(j + 1) % m2]) < cal(tu[i], tu[ni], nei[j])) {
int j1 = j;
j = (j + 1) % m2;
}
mi = min(mi, cal(tu[i], tu[ni], nei[j]));
}
ll ans = area2(tu) - mi;
cout << ans << "\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(10);
int T = 1;
cin >> T;
while (T--) solve();
return 0;
}