ACM-ICPC 2018 南京区域赛 D. Country Meow 三分 | 模拟退火

本文探讨了在三维坐标系中寻找一个点到多个已知点的最远距离最小的问题,提供了两种解决方案:三分法和模拟退火算法。三分法利用答案函数的单峰特性,在三个维度上独立搜索最优解;模拟退火算法则通过随机搜索和接受概率调整,逐步逼近全局最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解

题目大意给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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值