大致题意
X * Y 的地图上有 M 个陷阱,求地图上某一点的坐标,使离它最近陷阱的距离最大。1 <= X,Y <=10000, 1 <= M <= 1000
直接模拟退火,状态转移要足够随机,这里的做法是,以当前温度作为转移向量的模值,每一模值随机选取多个角度。初始结果选取较多的点,可以跳过局部最优化概率转移这一步。
然后是调参,初始温度和收缩因子决定了退火过程的耗时。初始温度设置一个小于可转移范围的值,收缩因子一般 0.8 ~ 0.99 ,太小精度会不够,太大会超时,这里 0.85 ~ 0.9 比较合适。
#include <cstdio>
#include <STDLIB.H>
#include <cmath>
#include <algorithm>
#include <utility>
#include <ctime>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define abs(x) ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-3
#define M_PI 3.14159265358979323846
#define MAX_M 1000
using namespace std;
struct point{
double x, y, d;
};
const int pnum = 30;
int M;
double X, Y;
double xi[MAX_M], yi[MAX_M];
point ps[pnum];
double dis(double x, double y){
double res = INF;
for(int i = 0; i < M; i++){
double dx = x - xi[i], dy = y - yi[i];
res = min(res, sqrt(dx * dx + dy * dy));
}
return res;
}
bool judge(double x, double y){
return x >= 0 && x <= X && y >= 0 && y <= Y;
}
int main(){
srand((unsigned)time(NULL));
int t;
scanf("%d", &t);
while(t--){
scanf("%lf%lf%d", &X, &Y, &M);
for(int i = 0; i < M; i++) scanf("%lf%lf", xi + i, yi + i);
//初始化可能解
for(int i = 0 ; i < pnum; i++){
ps[i].x = rand() % (int)X, ps[i].y = rand() % (int)Y;
ps[i].d = dis(ps[i].x, ps[i].y);
}
//设置初始温度
double T = sqrt(max(X, Y) / 10.0);
while(T > eps){
for(int i = 0; i < pnum; i++){
for(int j = 0; j < pnum; j++){
double r = (rand() % 360) /180.0 * M_PI;
double nx = ps[i].x + T * cos(r), ny = ps[i].y + T * sin(r);
if(!judge(nx, ny)) continue;
double d = dis(nx, ny);
if(d > ps[i].d){
ps[i].x = nx, ps[i].y = ny, ps[i].d = d;
}
}
}
T *= delta;
}
double x, y, res = 0;
for(int i = 0; i < pnum; i++){
if(res < ps[i].d){
x = ps[i].x, y = ps[i].y, res = ps[i].d;
}
}
printf("The safest point is (%.1f, %.1f).\n", x, y);
}
return 0;
}