A. Weird Flecks, But OK (计算几何&三维最小圆覆盖)(2021年度训练联盟热身训练赛第一场)

传送门

题意:一个三维立方体中有 n 个瑕疵点,现希望能从某个平面垂直向下切一个圆能把所有瑕疵都去除,需要找到这个一次能去除所有点的最小圆直径,即找到三个维度上的最小圆覆盖

思路:比常规的最小圆覆盖多了一个维度,分三个方向求解三次取最小答案即可。

代码实现1:

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ll long long
#define int long long
#define pii pair<int, int>
#define lowbit(x) (x &(-x))
#define ls(x) x<<1
#define rs(x) (x<<1+1)
#define me(ar) memset(ar, 0, sizeof ar)
#define mem(ar,num) memset(ar, num, sizeof ar)
#define rp(i, n) for(int i = 0, i < n; i ++)
#define rep(i, a, n) for(int i = a; i <= n; i ++)
#define pre(i, n, a) for(int i = n; i >= a; i --)
#define IOS ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
const int way[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
using namespace std;
const int  inf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-12;
const ll   mod = 1e9+7;
const int  N = 2e5 + 5;

inline void read(int &x){
    char t=getchar();
    while(!isdigit(t)) t=getchar();
    for(x=t^48,t=getchar();isdigit(t);t=getchar()) x=x*10+(t^48);
}

int n;
double a[N][5], r, ox, oy;

double dis(double ax, double ay, double bx, double by){
    return sqrt((ax-bx)*(ax-bx)+(ay-by)*(ay-by));
}

void get(double ax, double ay, double bx, double by, double cx, double cy){
    double x1 = ax-bx, y1 = ay-by;
    double d1 = (ax*ax-bx*bx+ay*ay-by*by)/2;
    double x2 = ax-cx, y2 = ay-cy;
    double d2 = (ax*ax-cx*cx+ay*ay-cy*cy)/2;
    ox = (d1*y2-y1*d2)/(x1*y2-y1*x2);
    oy = (d2*x1-x2*d1)/(x1*y2-y1*x2);
    r = dis(ox, oy, ax, ay);
}

double solve(int u, int v){

    ox = a[1][u], oy = a[1][v], r = 0;
    for(int i = 2; i <= n; i ++){
        if(dis(ox, oy, a[i][u], a[i][v])>r+eps){
            ox = a[i][u], oy = a[i][v], r = 0;
            for(int j = 1; j < i; j ++){
                if(dis(ox, oy, a[j][u], a[j][v])>r+eps){
                    ox = (a[i][u]+a[j][u])/2;
                    oy = (a[i][v]+a[j][v])/2;
                    r = dis(ox, oy, a[j][u], a[j][v]);
                    for(int k = 1; k < j; k ++){
                        if(dis(ox, oy, a[k][u], a[k][v])>r+eps){
                            get(a[i][u], a[i][v], a[j][u], a[j][v], a[k][u], a[k][v]);
                        }
                    }
                }
            }
        }
    }
    return r*2;
}

signed main()
{
//    IOS;

    cin >> n;
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= 3; j ++){
            cin >> a[i][j];
        }
    }
    random_shuffle(a+1, a+n+1);
    double ans = min(solve(1, 2), min(solve(1, 3), solve(2, 3)));
    printf("%.10f\n", ans);

    return 0;
}

代码实现2:(该方法还不是太理解)

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ll long long
#define int long long
#define pii pair<int, int>
#define lowbit(x) (x &(-x))
#define ls(x) x<<1
#define rs(x) (x<<1+1)
#define me(ar) memset(ar, 0, sizeof ar)
#define mem(ar,num) memset(ar, num, sizeof ar)
#define rp(i, n) for(int i = 0, i < n; i ++)
#define rep(i, a, n) for(int i = a; i <= n; i ++)
#define pre(i, n, a) for(int i = n; i >= a; i --)
#define IOS ios::sync_with_stdio(0); cin.tie(0);cout.tie(0);
const int way[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
using namespace std;
const int  inf = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-8;
const ll   mod = 1e9+7;
const int  N = 2e5 + 5;

inline void read(int &x){
    char t=getchar();
    while(!isdigit(t)) t=getchar();
    for(x=t^48,t=getchar();isdigit(t);t=getchar()) x=x*10+(t^48);
}

int n;
double a[N][5];

double solve(int u, int v)
{
     //初始化答案和误差值,这里的误差值需要特殊考虑,如果大小不够可能还没找到正确答案
    double res = inf, delta = 100;
    double x = 0, y = 0;  //x,y即圆心坐标
    while(delta>eps){
        int k = 0; double d = 0;
        for(int i = 1; i <= n; i ++){
            //hypot()计算直角三角形的斜边长度,相当于 sqrt(x*x, y*y)
            double dis = hypot(a[i][u]-x, a[i][v]-y);
            if(dis>d){
                d = dis;
                k = i;
            }
        }
        res = min(res, d);
        x += (a[k][u]-x)/d*delta;
        y += (a[k][v]-y)/d*delta;
        delta *= 0.99;
    }
    return res*2;
}

signed main()
{
//    IOS;

    cin >> n;
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= 3; j ++){
            cin >> a[i][j];
        }
    }

    //从三个平面维度上寻找最小覆盖圆
    double ans = min(solve(1, 2), min(solve(1, 3), solve(2, 3)));

    printf("%.10f\n", ans);

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值