题解
题目大意给n个三维坐标系的点,要求找到一个点到达这些点的最远距离最近。
三分法:
因为答案函数单峰且三个维度的最优解相互独立,所以使用三次三分嵌套求出最优的x、y、z。
模拟退火:
玄学算法。。随机选取一个点检测是否更优然后更新答案。每次选取的点随着温度的降低与当前最优解的偏移量也降低。由于答案函数单峰所以更容易得到最优解。
AC代码
三分法:
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 110;
int x[N], y[N], z[N];
int n;
double dis(double X, double Y, double Z)
{
double res = 0;
for (int i = 0; i < n; ++i)
res = max(res, sqrt((x[i] - X) * (x[i] - X) + (y[i] - Y) * (y[i] - Y) + (z[i] - Z) * (z[i] - Z)));
return res;
}
double solve(int d, double *p)
{
if (d == 3)
return dis(p[0], p[1], p[2]);
double l = -1e5, r = 1e5, res = INF;
while (abs(l - r) > 1e-3)
{
double m1 = l + (r - l) / 3, m2 = r - (r - l) / 3;
p[d] = m1;
double res1 = solve(d + 1, p);
p[d] = m2;
double res2 = solve(d + 1, p);
if (res1 > res2)
l = m1, res = res2;
else
r = m2, res = res1;
}
return res;
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
cin >> n;
for (int i = 0; i < n; ++i)
scanf("%d%d%d", &x[i], &y[i], &z[i]);
double p[3];
printf("%.10f\n", solve(0, p));
return 0;
}
模拟退火:
#include <stdio.h>
#include <bits/stdc++.h>
#define fst first
#define sed second
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 110;
int x[N], y[N], z[N];
int n;
double X, Y, Z, ans;
double dis(double X, double Y, double Z)
{
double res = 0;
for (int i = 0; i < n; ++i)
res = max(res, sqrt((x[i] - X) * (x[i] - X) + (y[i] - Y) * (y[i] - Y) + (z[i] - Z) * (z[i] - Z)));
return res;
}
void SA()
{
double T = 4, D = 0.995; //初始温度 退火率
while (T > 1e-8) //灭了
{
double x = X + ((rand() << 1) - RAND_MAX) * T; //根据最优解随机一个变动值随着T缩小范围
double y = Y + ((rand() << 1) - RAND_MAX) * T;
double z = Z + ((rand() << 1) - RAND_MAX) * T;
double res = dis(x, y, z);
if (res < ans) //更新最优解
ans = res, X = x, Y = y, Z = z;
else if (exp((ans - res) / T) * RAND_MAX > rand()) //概率接受当前解
X = x, Y = y, Z = z;
T *= D; //退火
}
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
srand(time(NULL) * 11);
rand();
cin >> n;
for (int i = 0; i < n; ++i)
scanf("%d%d%d", &x[i], &y[i], &z[i]), X += x[i], Y += y[i], Z += z[i];
X /= n, Y /= n, Z /= n; //取平均值
ans = dis(X, Y, Z);
SA();
printf("%.10f\n", ans);
return 0;
}