【参考博客】
https://www.cnblogs.com/bztMinamoto/p/10698920.html
【定义】
【圆】一个圆心和他的半径,就能够确定这个半径
【解决问题】
字面意思
给定n个点,求一个直径最小的能覆盖所有点的圆
【luogu P1742】
【算法学习】
最小覆盖圆上一定会有几个点,因为这样才能使结果最优
如果没有点在圆上,缩小半径一定是可取的
所以我们其实可以直接简单的循环三次选三个点求其外交圆?
显然是不行的
我们一个一个点向区域中添加,就像凸包一样
假设我们已经求取出了包含 前 i - 1 点的最小圆
然后添加第 i 个点
如果在圆内,不用管
如果不再圆内,进行以下操作
1. 重新确定圆,令 r = 0 ,圆心为这个点
2. 然后重新在前 i - 1 个点中找最小覆盖圆
令 j 从1 ~ i - 1 ,把两个点的连线为直径做圆
3. 再枚举第三个点,如果这个点在这个新的覆盖圆外
则求三个点的外交圆
这样一圈枚举下来就能求出新的最小覆盖圆了
一些细节
如何求三个点最小覆盖圆
求三个点组成三角形的两个连线中任意两条直线的
中垂线的交点即是圆心
中垂线,以直线终点为起点,法线为方向
建直线即可
【模板题代码】
【luogu P1742】
题意如上
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int MAXN = 1e5 + 5; int n; struct V { double x, y; V() {} V(double a, double b) :x(a), y(b) {} V operator+(const V&b) const { return V(x + b.x , y + b.y);} V operator-(const V&b) const { return V(x - b.x , y - b.y);} double operator*(const V&b) const { return x * b.y - y * b.x; } double operator^(const V&b) const { return x * b.x + y * b.y; } //叉乘 V operator*(double b) const { return V(x * b , y*b); } V operator/(double b) const { return V(x / b , y /b); } V rot() { return V(-y, x); } double len() { return (x*x + y * y); } }p[MAXN],o; typedef V P; struct L { V s, t; L(P a,P b):s(a),t(b){} friend P cross(L a, L b) { return a.s + a.t*(b.t*(b.s - a.s)) / (b.t*a.t); } //求交点 }; P circle(P a,P b, P c) { return cross(L((a + b)*0.5, (b - a).rot()), L((a + c)*0.5, (c - a).rot())); } //求圆心 double r; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lf%lf", &p[i].x, &p[i].y); } random_shuffle(p + 1, p + 1 + n); r = 0, o = P(0 , 0); for (int i = 1; i <= n; i++) { if ((p[i] - o).len() > r) { o = p[i], r = 0; for(int j=1;j<=i-1;j++) if ((p[j] - o).len() > r) { o = (p[i] + p[j])*0.5, r = (p[j] - o).len(); for (int k = 1; k <= j - 1; k++) if ((p[k] - o).len() > r) o = circle(p[i], p[j], p[k]), r = (p[k] - o).len(); } } } printf("%.10lf\n%.10lf %.10lf", sqrt(r), o.x, o.y); return 0; }
【题目】
【luogu P4586 最小双圆覆盖】
【题目大意】n个点,现在给两个半径相同的圆去覆盖,求最小半径
(多组数据)