无线网络

无线网络

农夫约翰的农场可以看作一个二维平面。

农场中散布着 $n$ 头奶牛,每头奶牛的位置坐标已知。

农场中还建有 $2$ 个 wifi 基站,每个基站的位置坐标已知。

这 $n+2$ 个位置坐标两两不同。

第一个基站的有效覆盖范围 $r_{1}$ 和第二个基站的有效覆盖范围 $r_{2}$ 均可由约翰自由设定。

因为奶牛喜欢保持电子邮件联系,所以约翰希望所有奶牛都能被无线网络覆盖。

如果一头奶牛满足以下两个条件中的至少一个:

  1. 它到第一个基站的距离不超过 $r_{1}$
  2. 它到第二个基站的距离不超过 $r_{2}$

那么就视为它已被无线网络覆盖。

同时为了降低成本,约翰希望 $r_{1}^{2}+r_{2}^{2}$ 尽可能小。

请你计算 $r_{1}^{2}+r_{2}^{2}$ 的最小可能值。

输入格式

第一行包含 $5$ 个整数 $n,x_{1},y_{1},x_{2},y_{2}$,其中 $n$ 为奶牛数量,$\left( {x_{1},y_{1}} \right)$ 为第一个基站的坐标,$\left( {x_{2},y_{2}} \right)$ 为第二个基站的坐标。

接下来 $n$ 行,每行包含两个整数 $x_{i},y_{i}$,表示一头奶牛的位置坐标 $\left( {x_{i},y_{i}} \right)$。

输出格式

输出 $r_{1}^{2}+r_{2}^{2}$ 的最小可能值,答案四舍五入到个位。

数据范围

前 $4$ 个测试点满足 $1 \leq n \leq 10$。
所有测试点满足 $1 \leq n \leq 2000$,${−10}^{7} \leq x_{i},y_{i} \leq {10}^{7}$。

输入样例1:

2 -1 0 5 3
0 2
5 2

输出样例1:

6

输入样例2:

4 0 0 5 0
9 4
8 3
-1 0
1 4

输出样例2:

33

 

解题思路

  题目要求答案保留小数点后$0$位,其实答案一定是一个整数,这是因为所有的点都是整数点,对于其中一个基站,在以它为圆心的圆上(圆弧上)一定会有一头牛(点),如果这个圆上没有点,那么这个圆的半径就一定可以缩小。所以半径一定是两个整数点之间的距离的平方,是一个整数。

  一种做法是先枚举每一个点,这个点到两个基站的距离(这里的距离是带平方的,下同)分别是$d_1$和$d_2$,接着先选择$d_1$,即确定第$1$个基站的半径大小,第$2$个基站的半径记为$r$,然后再枚举其他点到第$1$个基站的距离,如果某个点到第$1$个基站的距离大于$d_1$,那么表示第$1$个基站不可以覆盖到这个点,这时第$2$个基站的半径就是这个点到第$2$个基站的距离与$r$取最大值。枚举完第$1$个基站后,再选择$d_2$,即确定第$2$个基站的半径大小,再枚举其他的点来确定第$1$个基站的半径大小,与上面的方法相同。

  时间复杂度为$O \left( n^2 \right)$,AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2010;
 7 
 8 LL d1[N], d2[N];
 9 
10 int main() {
11     int n, x1, y1, x2, y2;
12     scanf("%d %d %d %d %d", &n, &x1, &y1, &x2, &y2);
13     for (int i = 0; i < n; i++) {
14         int x, y;
15         scanf("%d %d", &x, &y);
16         d1[i] = 1ll * (x1 - x) * (x1 - x) + 1ll * (y1 - y) * (y1 - y);
17         d2[i] = 1ll * (x2 - x) * (x2 - x) + 1ll * (y2 - y) * (y2 - y);
18     }
19     
20     LL ret = 9e18;
21     for (int i = 0; i < n; i++) {
22         LL d = 0;   // 先确定第1个基站的半径,此时d表示第2个基站的半径
23         for (int j = 0; j < n; j++) {
24             if (i != j && d1[j] > d1[i]) d = max(d, d2[j]); // 第1个基站覆盖不了第j个点,更新d
25         }
26         ret = min(ret, d1[i] + d);
27         
28         d = 0;  // 然后确定第2个基站的半径,此时d表示第1个基站的半径
29         for (int j = 0; j < n; j++) {
30             if (i != j && d2[j] > d2[i]) d = max(d, d1[j]); // 第2个基站覆盖不了第j个点,更新d
31         }
32         ret = min(ret, d2[i] + d);
33     }
34     
35     printf("%lld", ret);
36     
37     return 0;
38 }

  还有一种思路是,由于圆上一定会有一个点,所以先求每个点到其中一个基站的距离,这里假设到第$1$个基站的距离,所以最多会有$n$种距离。然后枚举这$n$种距离,枚举完一种距离后,有一些点会被第$1$个基站覆盖,剩下的点都是不能被第$1$个基站覆盖的,此时另外一个基站就要把这些点覆盖,即另外一个基站的半径就可以确定下来了,就是这些不能被第$1$个基站覆盖的点中,到第$2$个基站的最大距离。

  因此可以先把所有点到第$1$个基站的距离进行降序排序,然后从大到小枚举第$1$个基站的半径。从大到小枚举的好处是,每次都可以让一个点落到第$1$个基站覆盖的范围外,相当于每次都从圆内往外抛出一个点,这个点就需要被第$2$个基站覆盖,相当于在另外一个基站需要覆盖的点集中加一个点,然后用抛出的这个点更新第$2$个基站的半径,就可以知道另外一个基站的最小半径是多少了。因此每枚举完第$1$个基站的半径后,另外一个基站的半径就可以通过$O \left( 1 \right)$的复杂度得到。

  如果一个圆上有多个点,那么按照上面的枚举方式这些在同一个圆上的点并不是同时抛出的,而是一个一个依次抛出,但这并不会影响答案,因为将其中一个点抛出时,只会让第$2$个基站的半径增大,并不会影响到之前确定的最小的半径。

  时间复杂度为$O \left( nlogn \right)$,AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 typedef pair<LL, int> PLI;
 6 
 7 const int N = 2010;
 8 
 9 LL d1[N], d2[N];
10 PLI p[N];
11 
12 int main() {
13     int n, x1, y1, x2, y2;
14     scanf("%d %d %d %d %d", &n, &x1, &y1, &x2, &y2);
15     for (int i = 0; i < n; i++) {
16         int x, y;
17         scanf("%d %d", &x, &y);
18         d1[i] = 1ll * (x1 - x) * (x1 - x) + 1ll * (y1 - y) * (y1 - y);
19         d2[i] = 1ll * (x2 - x) * (x2 - x) + 1ll * (y2 - y) * (y2 - y);
20         p[i] = {d1[i], i};
21     }
22     
23     sort(p, p + n, greater<PLI>());
24     
25     LL ret = 9e18, d = 0;
26     for (int i = 0; i < n; i++) {
27         ret = min(ret, p[i].first + d);
28         d = max(d, d2[p[i].second]);    // 另外一个点的半径就是与被抛出的点i到第2个基站的距离取最大值
29     }
30     
31     printf("%lld", min(ret, d));    // 最后表示第1个基站覆盖0个点,第2个基站覆盖所有点,对这种情况再取一个最小值
32     
33     return 0;
34 }

 

参考资料

  AcWing 4429. 无线网络(AcWing杯 - 周赛):https://www.acwing.com/video/3931/

posted @ 2022-06-05 15:46  onlyblues  阅读(161)  评论(0编辑  收藏  举报
Web Analytics