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;
}

浙公网安备 33010602011771号