ACM-ICPC 2018 南京區域賽 D. Country Meow 三分 | 模擬退火

題解

題目大意給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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章