【天梯赛题解极角排序疑似有误】

文章讨论了极角排序方法在解决特定竞赛题中可能遇到的问题,指出由于相邻向量可能不是最小面积三角形的边,导致排序策略不能保证正确答案。作者提到一种优化方案但复杂度高,寻求更高效的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

天梯赛题解极角排序疑似有误

L3-021 神坛 题目链接

首先放上极角排序的做法

大体思路是,每次选一个点作为原点,确定出 n − 1 n - 1 n1 个向量,将这 n − 1 n - 1 n1 个向量进行极角排序后,选相邻两个向量作为三角形的两条边,求出最小面积 S = ∣ c r o s s ( v i ,   v i + 1 ) ∣ 2 S = \frac{\lvert cross(v_i, \ v_{i + 1}) \rvert}{2} S=2cross(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 我一下,阿里嘎多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值