题意
给你n个向量,你可以改变他们的符号,使得这些向量之和的长度小于1.5e6。
思路
由于每个向量的模小于
r=106
r
=
10
6
,我们可以通过调整符号使得两个向量之和的模小于等于
2–√∗r≤1.5e6
2
∗
r
≤
1.5
e
6
。证明如下:
已知|a|≤r,|b|≤r,假设夹角为Θ,|a−b|2=|a|2+|b|2−2∗|a|∗|b|∗cosΘ
已
知
|
a
|
≤
r
,
|
b
|
≤
r
,
假
设
夹
角
为
Θ
,
|
a
−
b
|
2
=
|
a
|
2
+
|
b
|
2
−
2
∗
|
a
|
∗
|
b
|
∗
c
o
s
Θ
当Θ=90°时,|a−b|2最大为|a|2+|b|2,|a|2+|b|2≤2∗r2,所以|a−b|2≤2–√∗r
当
Θ
=
90
°
时
,
|
a
−
b
|
2
最
大
为
|
a
|
2
+
|
b
|
2
,
|
a
|
2
+
|
b
|
2
≤
2
∗
r
2
,
所
以
|
a
−
b
|
2
≤
2
∗
r
假设(X,Y)为最终给完符号之后的向量,对于当前向量来说,考虑他的符号为正或者负的情况,我们取加上当前向量使得总向量的模最小的那种符号。
所以我们按照这种方案贪心就好了,但是这样可能会出现一种(X,Y)的模为0,只剩下一个向量,若此向量模为
106
10
6
,这样就不符合题意了,所以我们采取随机化方法,每次对数组进行一次随机化直到找到答案。
随机化大法好!!!
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+5;
typedef long long ll;
int n, ans[MAXN];
struct Vector
{
int x, y, id;
}a[MAXN];
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d%d", &a[i].x, &a[i].y), a[i].id = i;
while (1)
{
ll X = 0, Y = 0;
for (int i = 0; i < n; i++)
{
if ((X+a[i].x)*(X+a[i].x) + (Y+a[i].y)*(Y+a[i].y) <=
(X-a[i].x)*(X-a[i].x) + (Y-a[i].y)*(Y-a[i].y))
{
ans[a[i].id] = 1;
X += a[i].x; Y += a[i].y;
}
else ans[a[i].id] = -1, X -= a[i].x, Y -= a[i].y;
}
if (X*X+Y*Y <= 1.5e6*1.5e6) break;
random_shuffle(a, a+n);
}
for (int i = 0; i < n; i++) printf("%d ", ans[i]);
return 0;
}
/*
3
999999 0
0 999999
999999 0
*/