天梯赛题解极角排序疑似有误
L3-021 神坛 题目链接
首先放上极角排序的做法
大体思路是,每次选一个点作为原点,确定出 n − 1 n - 1 n−1 个向量,将这 n − 1 n - 1 n−1 个向量进行极角排序后,选相邻两个向量作为三角形的两条边,求出最小面积 S = ∣ c r o s s ( v i , v i + 1 ) ∣ 2 S = \frac{\lvert cross(v_i, \ v_{i + 1}) \rvert}{2} S=2∣cross(vi, vi+1)∣。
下面是 w a wa wa 最后一个点的代码。
时间复杂度 O ( n 2 ) O(n^2) O(n2)
#include <bits/stdc++.h>
using LL = long long;
struct Point {
int x, y;
constexpr Point(): x{}, y{} {}
constexpr Point(int _x, int _y): x{_x}, y{_y} {}
constexpr void reduce() {
int d = std::gcd(x, y);
if (d != 0) {
x /= d, y /= d;
}
}
constexpr static int area(Point a) {
if (a.x >= 0 and a.y >= 0) {
return 1;
} else if (a.x < 0 and a.y >= 0) {
return 2;
} else if (a.x < 0 and a.y < 0) {
return 3;
}
return 4;
}
constexpr static double angle(Point a) {
return atan2(a.y, a.x);
}
constexpr double angle() {
return angle(*this);
}
constexpr static LL Getd2(Point lhs, Point rhs) { // 两点间距离的平方
int dx = lhs.x - rhs.x;
int dy = lhs.y - rhs.y;
return 1LL * dx * dx + 1LL * dy * dy;
}
constexpr static double Getd(Point lhs, Point rhs) { // 两点间距离
return std::sqrt(Getd2(lhs, rhs));
}
constexpr Point &operator+=(Point rhs) & {
x += rhs.x;
y += rhs.y;
return *this;
}
constexpr Point &operator-=(Point rhs) & {
x -= rhs.x;
y -= rhs.y;
return *this;
}
friend constexpr Point operator+(Point lhs, Point rhs) {
Point res = lhs;
res += rhs;
return res;
}
friend constexpr Point operator-(Point lhs, Point rhs) {
Point res = lhs;
res -= rhs;
return res;
}
friend constexpr LL operator*(Point lhs, Point rhs) { // 内积
LL res = 1LL * lhs.x * rhs.x + 1LL * lhs.y * rhs.y;
return res;
}
friend constexpr LL operator^(Point lhs, Point rhs) { // 叉乘
LL res = 1LL * lhs.x * rhs.y - 1LL * lhs.y * rhs.x;
return res;
}
constexpr static LL getd2(Point a) { // 到原点距离的平方
return a * a;
}
constexpr static double getd(Point a) { // 到原点的距离
return std::sqrt(getd2(a));
}
constexpr LL dist2() {
return getd2(*this);
}
constexpr double dist() {
return getd(*this);
}
constexpr LL dist2(int cx, int cy) {
auto c = Point(cx, cy);
return Getd2(*this, c);
}
constexpr double dist(int cx, int cy) {
auto c = Point(cx, cy);
return Getd(*this, c);
}
constexpr LL dist2(Point c) {
return Getd2(*this, c);
}
constexpr double dist(Point c) {
return Getd(*this, c);
}
friend constexpr bool operator<(Point lhs, Point rhs) {
int a1 = area(lhs), a2 = area(rhs);
if (a1 != a2) { // 先排象限
return a1 < a2;
}
LL t = lhs ^ rhs;
if (t != 0) { // 再排同象限的极角
return t > 0;
}
return getd2(lhs) < getd2(rhs); // 若极角相同则按照距离排序
}
friend constexpr bool operator==(Point lhs, Point rhs) {
return lhs.x == rhs.x and lhs.y == rhs.y;
}
friend constexpr bool operator!=(Point lhs, Point rhs) {
return lhs.x != rhs.x or lhs.y != rhs.y;
}
friend std::istream &operator>>(std::istream &is, Point &a) {
return is >> a.x >> a.y;
}
friend std::ostream &operator<<(std::ostream &os, Point &a) {
return os << a.x << " " << a.y;
}
};
constexpr LL inf = 4E18;
void solve() {
int n;
std::cin >> n;
std::vector<Point> p(n);
for (int i = 0; i < n; i += 1) {
std::cin >> p[i];
}
LL res = inf;
for (int i = 0; i < n; i += 1) {
std::vector<Point> b;
for (int j = 0; j < n; j += 1) {
if (i != j) {
b.push_back(p[j] - p[i]);
}
}
std::sort(begin(b), end(b)); // 极角排序
for (int j = 0; j < n - 1; j += 1) {
res = std::min(res, std::abs(b[j] ^ b[(j + 1) % (n - 1)]));
}
}
double ans = 0.5 * res;
std::cout << std::fixed << std::setprecision(3) << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int T = 1;
// std::cin >> T;
while (T -- ) {
solve();
}
return 0;
}
看上去是挺对的,但是琢磨琢磨还是有反例的,比如把最小三角形塞到整个平面的中心,其他向量都非常长,如下:
Input1
6
-5 4
5 4
0 2
-1 0
1 0
0 -4
Output1(这里如果不先分象限是可以过的,但是是因为 sort
使得乱序刚好满足两个最好的向量相邻了)
3.000
Answer1
2.000
Input2
6
0 -2
-1 0
1 0
0 50
-50 -50
50 -50
Output2(这个就是怎么排都是错的了)
26.000
Answer2
2.000
原因就是,相邻的不一定是最小的三角形,因为相邻的边可以很长,但是不相邻的边可以很短,而面积公式是向量叉乘除以 2 2 2,也就是平行四边形的面积,即 S = l e n ( v 1 ) × l e n ( v 2 ) × sin ( θ ) S = len(v_1) \times len(v_2) \times \sin(\theta) S=len(v1)×len(v2)×sin(θ), θ \theta θ 为 v 1 v_1 v1 和 v 2 v_2 v2 的夹角,这就导致长度小,夹角稍大的也可以成为最小三角形。
所以每次无法确定要往后扫多少个向量,极角排序无法通过此题(只是数据水了)。
想看一些其他想法的 x d xd xd 可以看看 这篇博客,提到了一些性质优化,但是复杂度仍然很高。
但是我比较菜,完全没有其他想法了(悲)。
有会正解的佬可以 c a l l call call 我一下,阿里嘎多。