CF995C Leaving the Bar

题目链接

解题思路

原始想法

看到这题的第一眼是贪心,策略显然——每次加入一个向量后距离原点尽量小。

但是有 hack 数据,比如:

3
1000000 0
-1 999999
600000 -600000

我们考虑到这个数据的第二个向量,按照贪心策略你会选择正方向走到 \((999999,999999)\)
但是这样的话第三个向量无论怎么选都是错的。

但实际上存在一组答案:\(c=\{1,-1,-1\}\)
换句话说,如果我们先考虑第三个向量再考虑第二个,我们就不会有这样的问题。

优化

上述 hack 数据给到了我们莫大的启发:如果贪心不正确,则调换向量顺序。

并且容易发现,这题如果是随机大数据的话,贪心正确率相当的高。

这里,一个随机化做法已经呼之欲出:

  • 对向量序列随机打乱,然后跑一遍贪心,如果正确则可以直接结束。
    把上述过程执行 \(500\) 遍,便可通过这题的数据。

总结

对于这一类随机化贪心的题来说,

  • 贪心的思路是本能反应。
  • 在思考贪心的正确性时,容易有一个 hack 贪心的想法。
  • 而有了 hack 数据,我们自然地会想如何才能不被卡掉。

这时就有了我们的随机化贪心做法。

代码实现

点击查看代码
#include <bits/stdc++.h>
#define FL(i, a, b) for (int i = (a); i <= (b); ++i)
#define FR(i, a, b) for (int i = (a); i >= (b); --i)
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
constexpr int N = 1e5 + 10, V = 1.5e6;
int n, id[N], ans[N];
pii p[N];
pii operator + (pii a, pii b) {
	return {a.fi + b.fi, a.se + b.se};
}
pii operator - (pii a, pii b) {
	return {a.fi - b.fi, a.se - b.se};
}
ll Len(pii a) {
	return (ll)a.fi * a.fi + (ll)a.se * a.se;
}
bool Check() {
	pii x = {};
	FL(i, 1, n) {
		auto t1 = x + p[id[i]], t2 = x - p[id[i]];
		if (Len(t1) < Len(t2)) {
			x = t1;
			ans[id[i]] = 1;
		} else {
			x = t2;
			ans[id[i]] = -1;
		}
	}
	return Len(x) <= (ll)V * V;
}
int main() {
	scanf("%d", &n);
	FL(i, 1, n) {
		scanf("%d %d", &p[i].fi, &p[i].se);
		id[i] = i;
	}
	FL(T, 1, 500) {
		random_shuffle(id + 1, id + n + 1);
		if (Check()) {
			FL(i, 1, n) {
				printf("%d%c", ans[i], " \n"[i == n]);
			}
			break;
		}
	}
	return 0;
}
posted @ 2025-04-17 13:32  徐子洋  阅读(14)  评论(0)    收藏  举报