題解
題目大意給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;
}