当前位置: 首页 > 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.dtcms.com/a/313197.html

相关文章:

  • 【CF】Day118——杂题 (随机哈希 / 思维 | 贪心 / DP | 位运算构造 | 状态压缩 + 建图 + 最短路 | 构造 | 贪心)
  • 使用纯Docker命令搭建多服务环境(Linux版)
  • Python篇---包
  • 在Ansys Mechanical中对磨损进行建模
  • 力扣经典算法篇-40-螺旋矩阵(方向遍历:方向数组+已访问元素集合)
  • 【ROS2】常用命令
  • 04.Redis 的多实例
  • 双八无碳小车设计【16张cad】三维图+设计说明书
  • 【C++ 初级工程师面试--5】inline内联函数特点 、和普通函数的区别、什么时候适合内联?
  • json-server 快速搭建本地 Mock 数据服务
  • Day23--回溯--39. 组合总和,40. 组合总和 II,131. 分割回文串
  • Android 之 MVC架构
  • 线段树学习笔记 - 摩尔投票问题
  • I2C基础
  • mybatis-plus从入门到入土(四):持久层接口之BaseMapper和选装件
  • PHP现代化全栈开发:前后端分离与API架构实践
  • uni-app学习笔记01-项目初始化及相关文件
  • Go语言常量
  • 11.消息队列
  • 计算机视觉CS231n学习(2)
  • 从马武寨穿越关山
  • ICCV 2025 | EPD-Solver:西湖大学发布并行加速扩散采样算法
  • p5.js 用 beginGeometry () 和 endGeometry () 打造自定义 3D 模型
  • 控制建模matlab练习06:比例积分控制-②PI控制器
  • 达梦数据库联机备份和脱机备份的区别
  • Centos7 安装Python3.11
  • 【Linux系统编程】进程信号
  • leecode2958 最多K个重复元素的最长子数组
  • 解决飞书文档中PDF文档禁止下载的问题
  • 提升工作效率的利器:Qwen3 大语言模型