1. 题目解析与需求拆解
这道华为秋招机试题的核心是:在二维平面上给定N个信号塔的坐标,要求找到一个点,使得该点到所有信号塔的最大距离最小化。这个问题在数学上被称为"最小覆盖圆问题"或"最小最大距离问题",在通信基站选址、物流中心规划等领域有广泛应用。
1.1 问题形式化描述
给定:
- N个信号塔的坐标 (x₁,y₁), (x₂,y₂), ..., (xₙ,yₙ)
要求:
- 找到一个点 (a,b),使得 max(√[(a-x₁)²+(b-y₁)²], ..., √[(a-xₙ)²+(b-yₙ)²]) 最小
1.2 实际应用场景
这个问题在通信网络规划中非常常见。比如:
- 5G基站选址时需要确保覆盖区域内所有用户设备的最大信号延迟最小
- 无人机充电站布置需要让任意位置的无人机都能在最短距离内找到充电站
- 应急广播系统需要确保任何位置都能接收到至少一个信号塔的广播
2. 算法思路分析
2.1 暴力解法及其局限性
最直观的想法是枚举平面上所有可能的点,计算每个点到所有信号塔的最大距离,然后取最小值。但这种方法:
- 时间复杂度极高(无限多个点)
- 无法在有限时间内得到精确解
2.2 几何解法:最小覆盖圆
这个问题在计算几何中有标准解法——Welzl算法,可以在O(n)时间复杂度内找到最小覆盖圆。其核心思想是:
- 随机排列所有点
- 初始时圆为空
- 对于每个点,如果不在当前圆内,则将该点作为新圆上的点
- 递归处理前面的点
2.3 数值解法:三分搜索
对于编程竞赛更实用的方法是三分搜索:
- 先固定x坐标,对y坐标进行三分搜索找到当前x下的最优y
- 再对x坐标进行三分搜索
- 通过双重三分逼近最优解
这种方法时间复杂度约为O(log²(1/ε)),其中ε是精度要求。
3. Java实现与解析
import java.util.*; public class Main { static class Point { double x, y; Point(double x, double y) { this.x = x; this.y = y; } } static Point[] points; static final double EPS = 1e-8; public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); points = new Point[n]; for(int i=0; i<n; i++) { double x = sc.nextDouble(); double y = sc.nextDouble(); points[i] = new Point(x, y); } // 三分搜索x坐标 double left = -1e4, right = 1e4; while(right - left > EPS) { double mid1 = left + (right - left)/3; double mid2 = right - (right - left)/3; if(calc(mid1) > calc(mid2)) { left = mid1; } else { right = mid2; } } double bestX = (left + right)/2; double bestY = findY(bestX); double minDist = maxDistance(bestX, bestY); System.out.printf("%.2f", minDist); } // 给定x,找到最优y static double findY(double x) { double left = -1e4, right = 1e4; while(right - left > EPS) { double mid1 = left + (right - left)/3; double mid2 = right - (right - left)/3; if(maxDistance(x, mid1) > maxDistance(x, mid2)) { left = mid1; } else { right = mid2; } } return (left + right)/2; } // 计算给定x时的最小最大距离 static double calc(double x) { double y = findY(x); return maxDistance(x, y); } // 计算点(x,y)到所有信号塔的最大距离 static double maxDistance(double x, double y) { double max = 0; for(Point p : points) { double dx = x - p.x; double dy = y - p.y; max = Math.max(max, Math.sqrt(dx*dx + dy*dy)); } return max; } }3.1 关键点解析
- 三分搜索实现:对x和y坐标分别进行三分搜索,逐步缩小最优解范围
- 精度控制:使用EPS=1e-8作为终止条件,确保结果精确到小数点后两位
- 函数分解:
maxDistance()计算给定点到所有信号塔的最大距离findY()对给定x坐标找到最优y坐标calc()封装双重三分搜索过程
3.2 复杂度分析
- 时间复杂度:O(n log²(1/ε)),其中n是信号塔数量,ε是精度要求
- 空间复杂度:O(n),用于存储信号塔坐标
4. C++实现与解析
#include <iostream> #include <vector> #include <cmath> #include <iomanip> using namespace std; const double EPS = 1e-8; struct Point { double x, y; Point(double x=0, double y=0):x(x),y(y){} }; vector<Point> points; double max_distance(double x, double y) { double max_dist = 0; for(auto& p : points) { double dx = x - p.x; double dy = y - p.y; max_dist = max(max_dist, sqrt(dx*dx + dy*dy)); } return max_dist; } double find_y(double x) { double left = -1e4, right = 1e4; while(right - left > EPS) { double mid1 = left + (right - left)/3; double mid2 = right - (right - left)/3; if(max_distance(x, mid1) > max_distance(x, mid2)) { left = mid1; } else { right = mid2; } } return (left + right)/2; } double calc(double x) { double y = find_y(x); return max_distance(x, y); } int main() { int n; cin >> n; points.resize(n); for(int i=0; i<n; i++) { cin >> points[i].x >> points[i].y; } // 三分搜索x坐标 double left = -1e4, right = 1e4; while(right - left > EPS) { double mid1 = left + (right - left)/3; double mid2 = right - (right - left)/3; if(calc(mid1) > calc(mid2)) { left = mid1; } else { right = mid2; } } double best_x = (left + right)/2; double best_y = find_y(best_x); double min_dist = max_distance(best_x, best_y); cout << fixed << setprecision(2) << min_dist << endl; return 0; }4.1 C++特性利用
- 结构体构造:使用构造函数简化Point对象的创建
- IO优化:
fixed和setprecision控制输出格式 - STL容器:使用vector存储点集,方便动态调整大小
4.2 性能考虑
C++实现通常比Java更快,特别是:
- 避免Java的自动装箱/拆箱
- 更直接的内存访问
- 更高效的数学函数实现
5. Python实现与解析
import math def main(): import sys input = sys.stdin.read data = input().split() n = int(data[0]) points = [] index = 1 for _ in range(n): x = float(data[index]) y = float(data[index+1]) points.append((x, y)) index += 2 EPS = 1e-8 def max_distance(x, y): max_dist = 0 for (px, py) in points: dx = x - px dy = y - py dist = math.sqrt(dx*dx + dy*dy) if dist > max_dist: max_dist = dist return max_dist def find_y(x): left, right = -1e4, 1e4 while right - left > EPS: mid1 = left + (right - left)/3 mid2 = right - (right - left)/3 if max_distance(x, mid1) > max_distance(x, mid2): left = mid1 else: right = mid2 return (left + right)/2 def calc(x): y = find_y(x) return max_distance(x, y) # 三分搜索x坐标 left, right = -1e4, 1e4 while right - left > EPS: mid1 = left + (right - left)/3 mid2 = right - (right - left)/3 if calc(mid1) > calc(mid2): left = mid1 else: right = mid2 best_x = (left + right)/2 best_y = find_y(best_x) min_dist = max_distance(best_x, best_y) print("{0:.2f}".format(min_dist)) if __name__ == "__main__": main()5.1 Python实现特点
- 输入处理:使用
sys.stdin.read快速读取所有输入,适用于编程竞赛环境 - 嵌套函数:利用Python的嵌套函数特性,使代码结构更清晰
- 精度控制:虽然Python浮点数精度足够,但仍需注意EPS的合理设置
5.2 性能优化建议
对于大规模数据:
- 可以考虑使用NumPy数组存储点集
- 使用向量化运算替代循环
- 对于特别大的n,可能需要更高效的算法
6. 测试用例设计
6.1 基础测试用例
3 0 0 3 0 0 4预期输出:2.50 解释:最优点在(1.5,2),最大距离为2.5
6.2 边界情况
1 5 5预期输出:0.00 解释:只有一个信号塔,最优位置就是信号塔本身
6.3 大规模测试
10 1.2 3.4 5.6 7.8 9.0 1.2 3.4 5.6 7.8 9.0 2.3 4.5 6.7 8.9 0.1 2.3 4.5 6.7 8.9 0.1预期输出:5.00(近似值,实际需要计算)
7. 算法优化与变种
7.1 迭代优化法
除了三分搜索,还可以使用梯度下降等迭代方法:
- 随机初始化一个点
- 计算当前点到各信号塔的距离梯度
- 沿着梯度方向更新点位置
- 重复直到收敛
7.2 加权最小最大距离
实际问题中,不同信号塔可能有不同权重:
- 目标变为最小化 max(wᵢ·distance(p,pᵢ))
- 算法需要相应调整,但基本思路类似
7.3 高维扩展
在三维空间中(如无人机基站布置):
- 需要增加对z坐标的搜索
- 基本算法框架不变,但计算量会增加
8. 华为OD机考注意事项
- 输入输出格式:严格按照题目要求,包括小数点位数
- 时间限制:Python实现可能需要注意优化,避免超时
- 边界检查:考虑n=1等特殊情况
- 代码风格:保持整洁,适当注释,方便阅卷
提示:在实际机考中,建议先写暴力解法确保正确性,再优化为高效算法。三分搜索的实现需要特别注意终止条件和更新规则,避免无限循环。