Weird Flecks, But OK(最小圆覆盖、计算几何)

链接
题意:三维坐标系中,给定 n n n个点,要求用电钻垂直立方体表面进入,同时穿过的体积包含了所有点。问电钻的最小半径 r r r
思路:电钻进入一共有三个方向,也就是分别在 x o y , x o z , y o z xoy,xoz,yoz xoy,xoz,yoz三个平面上进行投影,在二维坐标上寻找最小圆覆盖。因此问题就转化成了三次最小圆覆盖
最小圆覆盖算法思路:(min_circle_cover函数)

  1. 设定初始圆心与半径(默认 r = 0 , o = a 0 r=0,o=a_0 r=0o=a0
  2. 在余下点集中寻找(for循环遍历),将点加入点集。
  • 如果新的点 a i a_i ai不在当前最小圆中 ⇒ \Rightarrow for循环遍历 0   i − 1 0~i-1 0 i1的点集
    • 如果第二重循环中的旧点集中 a j a_j aj不在当前最小圆中 ⇒ \Rightarrow a i a_i ai a j a_j aj一定在新的最小圆上。以他们为直径做圆进行更新。同时进行第三重 0   j − 1 0~j-1 0 j1的for循环:
    • *如果点 a k a_k ak不在当前最小圆中,则可以确定 a k a_k ak也在最小覆盖圆上。由此, a k a_k ak a i a_i ai a j a_j aj三点确定了最小覆盖圆。进行最小圆更新。

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long int
const int MAXN = 2e5 + 100;
int n;
struct Point3
{
    
    
    double x,y,z;
    Point3(){
    
    }
    void get()
    {
    
    
        scanf("%lf%lf%lf",&x,&y,&z);

    }

}N[5000 + 100];
const double eps = 1e-8;
int cmp(double x)
{
    
    
    if(fabs(x) < eps)   return 0;
    if(x > 0)   return 1;
    else        return -1;

}
struct point
{
    
    
    double x,y;
    point() {
    
    }
    point(double a, double b)
    {
    
    
        x = a;
        y = b;
    }
    friend point operator + (point a, point b)
    {
    
    
        return point(a.x + b.x, a.y + b.y);
    }
    friend point operator - (point a, point b)
    {
    
    
        return point(a.x - b.x, a.y - b.y);
    }
    double norm()
    {
    
    
        return sqrt(x*x + y*y);

    }

}M[MAXN];

void circle_center(point p0, point p1,point p2, point &cp)
{
    
    
    double a1 = p1.x - p0.x,b1=p1.y-p0.y,c1=(a1*a1+b1*b1) / 2;
    double a2 = p2.x - p0.x,b2=p2.y-p0.y,c2=(a2*a2+b2*b2) / 2;
    double d = a1 * b2 - a2 * b1;
    cp.x = p0.x + (c1*b2 - c2 * b1)/d;
    cp.y = p0.y + (a1*c2 - a2*c1) / d;
}
void circle_center(point p0, point p1, point& cp)
{
    
    
    cp.x = (p0.x + p1.x) / 2;
    cp.y = (p0.y + p1.y) / 2;


}
point center;
double radius;

bool point_in(const point &p)
{
    
    
    return cmp((p-center).norm() - radius) < 0;

}
void min_circle_cover(point a[], int n)
{
    
    
    radius = 0;
    center = a[0];
    for(int i = 1;i < n;++i)
    {
    
    
        if(!point_in(a[i]))
        {
    
    
            center = a[i];radius = 0;
            for(int j = 0;j < i;++j) if(!point_in(a[j])){
    
    
                circle_center(a[i],a[j], center);
                radius = (a[j] - center).norm();
                for(int k = 0;k < j;k++)    if(!point_in(a[k])){
    
    
                    circle_center(a[i], a[j], a[k], center);
                    radius = (a[k] - center).norm();
                }
            }
        }


    }


}
int main()
{
    
    
    scanf("%d",&n);
    for(int i = 0;i < n;++i)
        N[i].get();
    //X-Y面上的最小圆
    for(int i = 0;i < n;++i)
        M[i] = point(N[i].x, N[i].y);
    double rxy;
    min_circle_cover(M, n);
    rxy = radius;

    //YZ面上的最小圆
    for(int i = 0;i < n;++i)
        M[i] = point(N[i].y, N[i].z);
    double ryz;
    min_circle_cover(M, n);
    ryz = radius;

    //XZ面上的最小圆
    for(int i = 0;i < n;++i)
        M[i] = point(N[i].x, N[i].z);
    double rxz;
    min_circle_cover(M, n);
    rxz = radius;

    double ans = min(rxy, min(rxz,ryz));
    //printf("%lf %lf %lf\n", rxy,rxz, ryz);
    printf("%.10lf\n", ans*2);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44986601/article/details/114551218