洛谷 SP34 RUNAWAY - Run Away

SP34 RUNAWAY - Run Away

这道题和那道平衡点吊打XXX有点像。

不同的是那道题中的每个点都有权值,而这道题没有,然后求的东西也不太一样,不过万变不离其宗,还是可以用模拟退火做。

而且这道题的数据比较弱,所以随便调参基本都可以过,不像其他题目的参数极度看脸, 是一道对欧洲人与非洲人都十分友好的模拟退火入坑题。
具体步骤见下面代码注释:

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
//Mystery_Sky
//
#define M 1000100
#define INF 0x3f3f3f3f
struct node{
	int x, y;
}point[M];
int t, n, X, Y;
const double delta = 0.993;//降温系数
double ans_x, ans_y;

inline double dist(double x, double y)//求出题中要求的距离所有点的最小值
{
	double maxx = INF;
	for(int i = 1; i <= n; i++) {
		double dx = point[i].x - x;
		double dy = point[i].y - y;
		double dis = sqrt(dx * dx + dy * dy);
		maxx = min(dis, maxx);
	}
	return maxx;
}

inline void SA()
{
	double x = ans_x, y = ans_y;
	double T = 2000;//初始温度。
	double Ans = 0;
	while(T > 1e-14) {
		double new_x = x + ((rand()<<1) - RAND_MAX) * T;
		double new_y = y + ((rand()<<1) - RAND_MAX) * T;
		if(new_x > X || new_y > Y || new_x < 0 || new_y < 0) {//若随机出的新答案不在题目约束的范围内,则跳过后续部分,重新生成新解。
			T *= delta;
			continue;
		}
		double new_ans = dist(new_x, new_y);
		double DE = Ans - new_ans;
		if(DE < 0) {//如果生成的新答案比之前得到的最优解更大,则接受这个新答案。
			ans_x = x = new_x;
			ans_y = y = new_y;
			Ans = new_ans;
		}
		else if(exp(-DE/T) * RAND_MAX > rand()) x = new_x, y = new_y;//否则以一定的概率接受这个新答案(模拟退火不同于爬山算法之处)。
		T *= delta;//降温。
	}
	
}

int main() {
	srand(19260817);
    srand(rand());
    srand(rand());//玄学种子
	scanf("%d", &t);
	while(t--) {
		int sum_x = 0;
		int sum_y = 0;
		scanf("%d%d%d", &X, &Y, &n);
		for(int i = 1; i <= n; i++) {
			scanf("%d%d", &point[i].x, &point[i].y);
			sum_x += point[i].x;
			sum_y += point[i].y;	
		}
		ans_x = (double)sum_x / n;
		ans_y = (double)sum_y / n;//沿用平衡点吊打XXX那道题的做法,继续使用所有点的平均值的点作为初始的答案。
		SA();
		SA();
		SA();
		printf("The safest point is (%.1lf, %.1lf).\n", ans_x, ans_y);	
	}
	return 0;
}

posted @ 2019-07-08 21:15  Mystery_Sky  阅读(285)  评论(0编辑  收藏  举报