无线网络

无线网络

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

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

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

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

第一个基站的有效覆盖范围 r1 和第二个基站的有效覆盖范围 r2 均可由约翰自由设定。

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

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

  1. 它到第一个基站的距离不超过 r1
  2. 它到第二个基站的距离不超过 r2

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

同时为了降低成本,约翰希望 r12+r22 尽可能小。

请你计算 r12+r22 的最小可能值。

输入格式

第一行包含 5 个整数 n,x1,y1,x2,y2,其中 n 为奶牛数量,(x1,y1) 为第一个基站的坐标,(x2,y2) 为第二个基站的坐标。

接下来 n 行,每行包含两个整数 xi,yi,表示一头奶牛的位置坐标 (xi,yi)

输出格式

输出 r12+r22 的最小可能值,答案四舍五入到个位。

数据范围

4 个测试点满足 1n10
所有测试点满足 1n2000107xi,yi107

输入样例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位,其实答案一定是一个整数,这是因为所有的点都是整数点,对于其中一个基站,在以它为圆心的圆上(圆弧上)一定会有一头牛(点),如果这个圆上没有点,那么这个圆的半径就一定可以缩小。所以半径一定是两个整数点之间的距离的平方,是一个整数。

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

  时间复杂度为O(n2),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(1)的复杂度得到。

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

  时间复杂度为O(nlogn),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 @   onlyblues  阅读(162)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示