算法学习:最小圆覆盖

【参考博客】

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;
}
View Code

 

 


【题目】

【luogu P4586 最小双圆覆盖】

【题目大意】n个点,现在给两个半径相同的圆去覆盖,求最小半径

(多组数据)

【题解】

 

转载于:https://www.cnblogs.com/rentu/p/11345493.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值