无线网络
无线网络
农夫约翰的农场可以看作一个二维平面。
农场中散布着 $n$ 头奶牛,每头奶牛的位置坐标已知。
农场中还建有 $2$ 个 wifi 基站,每个基站的位置坐标已知。
这 $n+2$ 个位置坐标两两不同。
第一个基站的有效覆盖范围 $r_{1}$ 和第二个基站的有效覆盖范围 $r_{2}$ 均可由约翰自由设定。
因为奶牛喜欢保持电子邮件联系,所以约翰希望所有奶牛都能被无线网络覆盖。
如果一头奶牛满足以下两个条件中的至少一个:
- 它到第一个基站的距离不超过 $r_{1}$
- 它到第二个基站的距离不超过 $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/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16344114.html