当前位置: 首页 > news >正文

最小半径覆盖问题【C++解法+二分+扫描线】

📌 最小半径覆盖问题【C++解法+二分+扫描线】

🧩 题目描述

在平面上给定 $n$ 个二维点,其坐标分别为 $(x_i, y_i)$。现在需要在坐标平面上以某一点为圆心画一个圆,且圆心必须位于坐标轴上(即 $x$ 轴或 $y$ 轴上),使得该圆能够覆盖不少于 $\lceil \frac{n}{2} \rceil$ 个点

请你求出满足条件的最小半径。


📥 输入格式

第一行输入一个整数 n,表示点的数量。接下来 n 行,每行输入两个整数 xi 和 yi,表示第 i 个点的坐标。

📤 输出格式

输出一个实数,表示满足要求的最小半径,保留 6 位小数。

✅ 判题要求

设你的输出为 $x$,标准答案为 $y$,当且仅当:

∣x−y∣≤10−6 |x - y| \leq 10^{-6} xy106

你的答案将被接受。


📘 输入示例 1

2
0 0
3 4

🎯 输出

0.000000

解释:取圆心为 (0, 0),半径为 0,即可覆盖第一个点,满足 $k = \lceil \frac{2}{2} \rceil = 1$。


📘 输入示例 2

4
1 0
2 0
3 0
100 100

🎯 输出

0.500000

解释:选圆心为 (1.5, 0),半径 0.5,能覆盖 (1, 0)、(2, 0),满足覆盖 $\geq 2$ 个点。


💡 解题思路

本题核心思想是:

二分半径 + 扫描线验证覆盖数量

Step1:半径二分查找

  • 定义二分查找区间:$[0, 3 \times 10^5]$
  • 每次取中值 $mid$,判断是否存在一个圆心在坐标轴上的圆半径为 $mid$,能覆盖 $\geq k = \lceil \frac{n}{2} \rceil$ 个点。

Step2:扫描线 + 区间合并判断可行性

对于一个给定半径 $r$,判断是否可以用一个圆心在某条坐标轴上的圆覆盖 $\geq k$ 个点:

  • 圆心在 $x$ 轴:若 $|y_i| \leq r$,则点 $(x_i, y_i)$ 可被横轴上的某个圆心覆盖,计算可行的 $x$ 范围区间。
  • 圆心在 $y$ 轴:若 $|x_i| \leq r$,则点 $(x_i, y_i)$ 可被纵轴上的某个圆心覆盖,计算可行的 $y$ 范围区间。
  • 将这些区间看作“事件”,利用扫描线统计同时覆盖最大点数是否 $\geq k$。

🧠 复杂度分析

  • 二分次数为 $O(\log_2(\text{精度})) \approx 60$
  • 每轮中,判断是否可行:$O(n \log n)$(排序 + 扫描)
  • 总体时间复杂度:$O(n \log n \log \text{精度})$

🧾 C++代码实现(附详细注释)

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;typedef pair<double, int> Event;// 检查是否存在一个半径为r的圆,能在坐标轴上找到圆心覆盖至少k个点
bool check(double r, const vector<pair<double, double>>& points, int k) {// 判断横轴或纵轴是否有可行解auto scan = [&](int axis) -> bool {vector<Event> events;for (const auto& [x, y] : points) {if (axis == 0) {  // 圆心在x轴上if (abs(y) > r) continue;double d = sqrt(r * r - y * y);events.emplace_back(x - d, 1);events.emplace_back(x + d, -1);} else {  // 圆心在y轴上if (abs(x) > r) continue;double d = sqrt(r * r - x * x);events.emplace_back(y - d, 1);events.emplace_back(y + d, -1);}}if ((int)events.size() < 2 * k) return false;// 排序:相同位置时,+1 比 -1 优先sort(events.begin(), events.end(), [](const Event& a, const Event& b) {return a.first == b.first ? a.second > b.second : a.first < b.first;});int cnt = 0;for (const auto& [pos, val] : events) {cnt += val;if (cnt >= k) return true;}return false;};return scan(0) || scan(1);
}int main() {int n;cin >> n;vector<pair<double, double>> points(n);for (int i = 0; i < n; ++i) {cin >> points[i].first >> points[i].second;}int k = (n + 1) / 2;  // 至少覆盖一半double lo = 0.0, hi = 3e5;for (int iter = 0; iter < 60; ++iter) {double mid = (lo + hi) / 2;if (check(mid, points, k))hi = mid;elselo = mid;}printf("%.6f\n", hi);return 0;
}

📎 总结

  • 本题属于几何优化 + 二分答案 + 扫描线结合的综合题。
  • 注意容错:由于实数精度问题,建议二分迭代 60 次。
  • C++ 中 sqrt 计算非常高效,printf("%.6f") 控制精度为关键!
http://www.xdnf.cn/news/1236691.html

相关文章:

  • 从零开始学Express,理解服务器,路由于中间件
  • 批发订货系统:私有化部署与源代码支持越来越受市场追捧
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-56,(知识点:电源模块,DCDC电源,LDO电源,原理及其特点)
  • CVE-2025-5947 漏洞场景剖析
  • SpringBoot3.x入门到精通系列:2.5 整合 MyBatis 详解
  • 井盖识别数据集-2,700张图片 道路巡检 智能城市
  • [硬件电路-134]:模拟电路 - 运算放大器常见运算:两模拟信号相加、相减、单模拟信号的积分、微分...
  • 如新能源汽车渗透率模拟展开完整报告
  • 老电脑PE下无法读取硬盘的原因
  • node.js常用函数
  • 【代码详解】Triplane Meets Gaussian Splatting中triplane部分解析
  • Nvidia Orin DK 刷机CUDA TensorRT+硬盘扩容+ROS+Realsense+OpenCV+Ollama+Yolo11 一站式解决方案
  • Unity_数据持久化_XML序列化与反序列化
  • Dify中自定义工具类的类型
  • 服务器中切换盘的操作指南
  • 更换KR100门禁读头&主机
  • Redis+Lua的分布式限流器
  • 专网内网IP攻击应急与防御方案
  • 专网内网IP攻击防御:从应急响应到架构加固
  • 一个网页的加载过程详解
  • 2025年EAAI SCI1区TOP,森林救援调度与路径规划:一种新型蚁群优化算法应用,深度解析+性能实测
  • MVCC:数据库事务隔离的 “时空魔法”
  • 著作权登记遇难题:创作者如何突破确权困境?
  • Rust:开发 DLL 动态链接库时如何处理 C 字符串
  • GaussDB SQL执行计划详解
  • Flutter各大主流状态管理框架技术选型分析及具体使用步骤
  • RAG-Semantic Chunking
  • 一加Ace5无法连接ColorOS助手解决(安卓设备ADB模式无法连接)
  • 迈向透明人工智能: 可解释性大语言模型研究综述
  • JavaScript 性能优化实战指南:从运行时到用户体验的全面提升​