资讯动态

华为秋招机试:最小覆盖圆算法与三分搜索实践

发布时间:2026/8/21 6:40:15 来源:尧图企业网站定制
1. 题目解析与需求拆解这道华为秋招机试题的核心是在二维平面上给定若干信号塔的坐标要求找到一个点使得该点到所有信号塔的最大距离最小化。换句话说我们需要在所有可能的点中找到一个位置使得离它最远的那个信号塔的距离尽可能小。这个问题在数学上被称为最小覆盖圆问题或者更准确地说是寻找一组点的最小外接圆。在实际工程应用中这相当于为多个信号塔寻找一个最优的中继站位置确保信号传输的最坏情况即最远距离被最小化。2. 算法思路分析2.1 暴力解法与复杂度分析最直观的解法是枚举所有可能的点计算每个点到所有信号塔的距离然后找出其中的最小值。然而平面上有无限多个点这种暴力解法显然不可行。即使我们只考虑信号塔之间的中点因为最优解很可能出现在这些位置对于n个信号塔需要考虑的组合数为O(n³)当n较大时题目中提到n≤1000这样的复杂度仍然难以接受。2.2 几何解法最小覆盖圆在计算几何中寻找一组点的最小覆盖圆有成熟的算法。最著名的是Welzl算法这是一种随机增量算法平均时间复杂度为O(n)。Welzl算法的基本思想是随机打乱所有点的顺序初始时圆为空对于每个点如果它不在当前圆内则将它作为边界点递归地计算其他点的最小覆盖圆这个算法在实践中表现良好但实现起来有一定难度特别是在处理边界条件时。2.3 数值解法三分搜索考虑到本题是在二维平面上寻找最优解我们可以采用数值优化的方法。具体来说可以分别在x轴和y轴方向上进行三分搜索。三分搜索的基本思路确定搜索范围所有信号塔的最小/最大x、y坐标在x方向上进行三分对每个x值在y方向上进行三分搜索对于每个(x,y)点计算到所有信号塔的最大距离不断缩小搜索范围直到达到精度要求这种方法的时间复杂度为O(n log(1/ε))其中ε是要求的精度对于题目中的精度要求1e-6来说非常合适。3. 代码实现与解析3.1 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 int n; public static void main(String[] args) { Scanner sc new Scanner(System.in); n sc.nextInt(); points new Point[n]; double minX Double.MAX_VALUE, maxX Double.MIN_VALUE; double minY Double.MAX_VALUE, maxY Double.MIN_VALUE; for (int i 0; i n; i) { double x sc.nextDouble(); double y sc.nextDouble(); points[i] new Point(x, y); minX Math.min(minX, x); maxX Math.max(maxX, x); minY Math.min(minY, y); maxY Math.max(maxY, y); } // 三分搜索 double lx minX, rx maxX; double ly minY, ry maxY; double res Double.MAX_VALUE; while (rx - lx 1e-7 || ry - ly 1e-7) { double mid1x lx (rx - lx) / 3; double mid2x rx - (rx - lx) / 3; double[] res1 ternarySearchY(mid1x, ly, ry); double[] res2 ternarySearchY(mid2x, ly, ry); if (res1[0] res2[0]) { rx mid2x; res Math.min(res, res1[0]); } else { lx mid1x; res Math.min(res, res2[0]); } } System.out.printf(%.6f\n, res); } static double[] ternarySearchY(double x, double ly, double ry) { double res Double.MAX_VALUE; double bestY 0; while (ry - ly 1e-7) { double mid1y ly (ry - ly) / 3; double mid2y ry - (ry - ly) / 3; double d1 maxDistance(x, mid1y); double d2 maxDistance(x, mid2y); if (d1 d2) { ry mid2y; res Math.min(res, d1); bestY mid1y; } else { ly mid1y; res Math.min(res, d2); bestY mid2y; } } return new double[]{res, bestY}; } static double maxDistance(double x, double y) { double max 0; for (Point p : points) { double dx x - p.x; double dy y - p.y; double dist Math.sqrt(dx * dx dy * dy); max Math.max(max, dist); } return max; } }3.2 C实现#include iostream #include vector #include cmath #include iomanip #include algorithm using namespace std; struct Point { double x, y; }; vectorPoint points; int n; double max_distance(double x, double y) { double max_dist 0; for (const auto p : points) { double dx x - p.x; double dy y - p.y; double dist sqrt(dx * dx dy * dy); max_dist max(max_dist, dist); } return max_dist; } pairdouble, double ternary_search_y(double x, double ly, double ry) { double res 1e18; double best_y 0; while (ry - ly 1e-7) { double mid1y ly (ry - ly) / 3; double mid2y ry - (ry - ly) / 3; double d1 max_distance(x, mid1y); double d2 max_distance(x, mid2y); if (d1 d2) { ry mid2y; res min(res, d1); best_y mid1y; } else { ly mid1y; res min(res, d2); best_y mid2y; } } return {res, best_y}; } int main() { cin n; points.resize(n); double min_x 1e18, max_x -1e18; double min_y 1e18, max_y -1e18; for (int i 0; i n; i) { cin points[i].x points[i].y; min_x min(min_x, points[i].x); max_x max(max_x, points[i].x); min_y min(min_y, points[i].y); max_y max(max_y, points[i].y); } double lx min_x, rx max_x; double ly min_y, ry max_y; double res 1e18; while (rx - lx 1e-7 || ry - ly 1e-7) { double mid1x lx (rx - lx) / 3; double mid2x rx - (rx - lx) / 3; auto [res1, y1] ternary_search_y(mid1x, ly, ry); auto [res2, y2] ternary_search_y(mid2x, ly, ry); if (res1 res2) { rx mid2x; res min(res, res1); } else { lx mid1x; res min(res, res2); } } cout fixed setprecision(6) res endl; return 0; }3.3 Python实现import math def main(): import sys input sys.stdin.read data input().split() idx 0 n int(data[idx]) idx 1 points [] min_x float(inf) max_x -float(inf) min_y float(inf) max_y -float(inf) for _ in range(n): x float(data[idx]) y float(data[idx1]) idx 2 points.append((x, y)) min_x min(min_x, x) max_x max(max_x, x) min_y min(min_y, y) max_y max(max_y, y) 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) max_dist max(max_dist, dist) return max_dist def ternary_search_y(x, ly, ry): res float(inf) best_y 0 while ry - ly 1e-7: mid1y ly (ry - ly) / 3 mid2y ry - (ry - ly) / 3 d1 max_distance(x, mid1y) d2 max_distance(x, mid2y) if d1 d2: ry mid2y res min(res, d1) best_y mid1y else: ly mid1y res min(res, d2) best_y mid2y return res, best_y lx, rx min_x, max_x ly, ry min_y, max_y res float(inf) while rx - lx 1e-7 or ry - ly 1e-7: mid1x lx (rx - lx) / 3 mid2x rx - (rx - lx) / 3 res1, y1 ternary_search_y(mid1x, ly, ry) res2, y2 ternary_search_y(mid2x, ly, ry) if res1 res2: rx mid2x res min(res, res1) else: lx mid1x res min(res, res2) print({0:.6f}.format(res)) if __name__ __main__: main()4. 算法优化与边界处理4.1 精度控制与终止条件在三分搜索中我们需要特别注意终止条件。对于本题要求输出结果精确到小数点后6位因此我们的搜索精度应该更高通常设为1e-7或1e-8。在实现中我们同时对x和y方向进行三分搜索因此需要确保两个方向的搜索都达到精度要求while (rx - lx 1e-7 || ry - ly 1e-7) { // 三分搜索过程 }4.2 避免重复计算计算点到所有信号塔的最大距离是一个O(n)的操作在三分搜索中会被频繁调用。我们可以通过以下方式优化将信号塔坐标存储在数组中避免重复访问复杂数据结构在Java/C中使用基本类型而非对象减少访问开销在Python中使用元组而非类来存储点坐标4.3 特殊情况处理需要考虑的特殊情况包括只有一个信号塔最小距离显然为0所有信号塔在同一直线上算法仍然适用浮点数精度问题确保使用double而非float5. 复杂度分析与性能比较5.1 时间复杂度三分搜索的时间复杂度取决于搜索范围和精度要求。假设初始搜索范围为D精度要求为ε则迭代次数为O(log(D/ε))。每次迭代需要O(n)的时间计算最大距离。因此总时间复杂度为O(n log(D/ε))。对于n≤1000和ε1e-7的情况这个复杂度是完全可接受的。5.2 空间复杂度我们只需要O(n)的空间存储信号塔坐标因此空间复杂度为O(n)。5.3 与其他算法的比较Welzl算法虽然理论复杂度更好O(n)但实现复杂常数因子大在实际中对于n1000可能不如三分搜索快。梯度下降另一种数值优化方法但需要调整学习率可能收敛到局部最优。模拟退火随机优化方法适用于更复杂的问题但本题有更高效的确定性算法。6. 实际应用与扩展6.1 在通信网络中的应用这个问题在实际通信网络规划中有重要应用。例如基站选址确保覆盖区域内所有用户的最差信号质量尽可能好无线传感器网络选择数据汇聚点的最优位置无人机基站部署寻找最佳悬停位置覆盖多个地面终端6.2 问题变种与扩展加权最小覆盖圆每个信号塔有不同的权重需要考虑加权距离障碍物约束在存在障碍物的情况下寻找最优位置动态场景信号塔位置随时间变化需要动态调整最优位置高维空间将问题扩展到三维或更高维空间6.3 在线测试与验证在实现这类算法时建议使用以下测试用例进行验证少量点2-3个的简单情况所有点共线的情况随机生成的大规模数据边界值如坐标非常大或非常小例如可以使用如下Python代码生成测试用例import random def generate_test_case(n): print(n) for _ in range(n): x random.uniform(-1e6, 1e6) y random.uniform(-1e6, 1e6) print(f{x:.6f} {y:.6f}) generate_test_case(1000)7. 面试技巧与注意事项7.1 解题思路阐述在面试中遇到此类问题时建议按以下步骤阐述明确问题重述问题确保理解正确分析暴力解法说明其不可行性提出优化思路几何性质或数学优化方法讨论算法选择比较不同方法的优缺点考虑边界情况特殊输入的处理分析复杂度时间和空间复杂度7.2 代码实现建议模块化设计将关键操作如距离计算封装为函数良好的命名使用有意义的变量名如min_x, max_y等注释关键步骤解释三分搜索的逻辑处理输入输出注意格式要求特别是精度7.3 常见错误与避免方法精度不足使用float而非double或终止条件不够严格解决方法始终使用double设置足够的精度余量无限循环终止条件设置不当解决方法确保同时检查x和y方向的收敛初始范围错误没有正确计算信号塔的边界解决方法先遍历所有点确定min_x, max_x等性能问题在内部循环中执行不必要的操作解决方法预先存储点坐标简化距离计算8. 总结与个人体会这道题目很好地考察了以下几个方面的能力将实际问题抽象为数学模型的能力对计算几何基本问题的了解数值优化算法的实现技巧边界条件和精度的处理在实际实现过程中我发现三分搜索虽然思路简单但要正确处理二维搜索并不容易。特别是在确定搜索范围和终止条件时需要仔细考虑。此外对于大规模数据n1000算法效率完全足够这验证了其在实际应用中的可行性。对于准备华为这类技术公司面试的求职者我的建议是熟练掌握基础算法如二分搜索、三分搜索等理解如何将实际问题转化为算法问题注意代码实现的细节和鲁棒性多练习在线编程题目适应机试环境最后这个问题还可以进一步优化比如结合梯度下降进行局部精细搜索或者并行化处理距离计算。这些优化在极端大规模数据下可能会有更明显的效果。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价