题意:
给出一个矩形作为边界,内部有n个点;
求一个点到这些点距离的最小值最大;
n<=1000;
题解:
最小值最大——二分答案!
然而并不能做到= =
正解似乎是一个叫做Voronoi的神奇东西;
不过写个20k的精确算法不如秒个2k的骗分算法A掉它嘛;
这道题我写的就是模拟退火;
首先设中点为起点似乎比较美观吧;
然后就是普通的退火过程,照着百度百科扒一扒那几句话就好;
(1) 初始化:初始温度T(充分大),初始解状态S(是算法迭代的起点), 每个T值的迭代次数L
(2) 对k=1,……,L做第(3)至第6步:
(3) 产生新解S′
(4) 计算增量Δt′=C(S′)-C(S),其中C(S)为评价函数
(5) 若Δt′<0则接受S′作为新的当前解,否则以概率exp(-Δt′/(KT))接受S′作为新的当前解(k为波尔兹曼常数).
(6) 如果满足终止条件则输出当前解作为最优解,结束程序。
(7) T逐渐减少,且T->0,然后转第2步。
OI里终止条件一般视为T<EPS就好;
不卡精度不卡时,良心一A题;
感觉自己成为欧洲人了23333;
代码:
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 11000
using namespace std;
const double EPS=1e-10;
const double INF=1e100;
struct Point
{
double x,y;
Point(){}
Point(double _,double __):x(_),y(__){}
void read()
{
scanf("%lf%lf",&x,&y);
}
friend Point operator +(Point a,Point b)
{
return Point(a.x+b.x,a.y+b.y);
}
friend Point operator -(Point a,Point b)
{
return Point(a.x-b.x,a.y-b.y);
}
friend double operator *(Point a,Point b)
{
return a.x*b.x+a.y*b.y;
}
friend double dis2(Point a,Point b)
{
return (a-b)*(a-b);
}
}p[N];
double X,Y;
int n;
double drand()
{
return rand()%10000/10000.0;
}
double calc(Point a)
{
double ret=INF;
for(int i=1;i<=n;i++)
{
ret=min(ret,dis2(a,p[i]));
}
return -ret;
}
void slove(Point& ans,double T)
{
Point next,dt;
double E,nE,dE;
while(T>EPS)
{
dt=Point((drand()*2-1.0)*T,(drand()*2-1.0)*T);
next=ans+dt;
next.x=min(X,max(0.0,next.x)),next.y=min(Y,max(0.0,next.y));
nE=calc(next);
dE=nE-E;
if(dE<0||drand()<exp(-dE/T))
ans=next,E=nE;
T*=0.99140142;
}
}
int main()
{
srand(140142);
int c,T,m,i,j,k;
Point ans;
scanf("%d",&T);
for(c=1;c<=T;c++)
{
scanf("%lf%lf%d",&X,&Y,&n);
for(i=1;i<=n;i++)
p[i].read();
ans=Point(X/2,Y/2);
slove(ans,max(X,Y)/2);
printf("The safest point is (%.1lf, %.1lf).\n",ans.x,ans.y);
}
return 0;
}