题意:一个三维立方体中有 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;
}