Run Away HDU - 1109
一种搜索算法,思想类似走山路,从某点开始不断往四周相邻更高的点走去,当走到某点已经是山峰顶,四周没有比当前点更高的点就停下,认为当前点是目标。
其中“高”是指点的估值,类似A*的那个意思。
但爬山法得出的一定是可行解,但不一定是最优解。
爬山演示
如图,如果从A点出发,则到达C点后因为四周没有比它更低的点就停止了,导致到不了真正的最高点D,而从B点出发却能到达最优解
模拟退火
爬山算法的改进,但仍然是随机算法,不一定求出最优解。跟爬山不同的是:即使四周没有更高的点,我们也以一定概率接受比当前点更低的点,这样就有机会摆脱局部最优而到达最优解,而这个概率会随时间不断减小。一定的概率是多大呢?参考热力学公式:在温度为T时,出现能量差为dE的降温的概率为P(dE)= edE/K∗T ,其中K是玻尔兹曼常数,约等于1。
本题思路:
考虑先从中间选出一个点,依次为起点每次选择30个点(也可以选更多)作为随机点,选择对角线长度为初始步长。每循环依次找到一个最大值,然后开始退火(step*=0.9),缩小步长,找到全局最优解。
代码
// hdu 1109
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8;
const double inf=1e20;
const double PI=acos(-1.0);
const int maxn=1e5+3;
int times;
int sgn(double x)
{
if(fabs(x)<eps) return 0;
if(x<0) return -1;
else return 1;
}
inline double square(double x) {return x*x;}
struct Point
{
double x,y;
Point()=default;
Point(double x_, double y_):x(x_),y(y_){}
void input(){scanf("%lf%lf",&x,&y);}
void output(){printf("%.2lf %.2lf\n",x,y);}
Point operator - (const Point &b) const
{
return Point(x-b.x,y-b.y);
}
bool operator==(Point b) const
{
return sgn(x-b.x)==0&&sgn(y-b.y)==0;
}
bool operator <(Point b) const
{
return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x;
}
Point operator + (const Point &b) const
{
return Point(x+b.x,y+b.y);
}
double operator ^ (const Point &b) const
{
return x*b.y-y*b.x;
}
double operator * (const Point &b) const
{
return x*b.x+y*b.y;
}
Point operator/(const double &k)
{
return Point(x/k,y/k);
}
double len(){return hypot(x,y);}
double len2(){return x*x+y*y;}
double distance(Point b){return hypot(x-b.x,y-b.y);}
}p[maxn];
double randNum(){ //rand()生成[0,32767),包装下生成[0,1)
return rand()%10000/10000.0;
}
double X,Y;
int n;
double minDis(Point tmp)
{
double ans=inf;
for(int i=0;i<n;i++)
{
ans=min(ans,p[i].distance(tmp));
}
return ans;
}
void Simulated(Point &ans,double step)
{
double T_min=eps;
double E=minDis(ans);
times=30;
while(step>T_min)
{
Point nxt;
double nE=0.f;
for(int i=0;i<times;i++)
{
Point tmp;
double angle=randNum()*2*PI;
tmp.x=ans.x+step*cos(angle);
tmp.y=ans.y+step*sin(angle);
tmp.x=min(X,max(0.0,tmp.x)),tmp.y=min(Y,max(0.0,tmp.y));
double tE=minDis(tmp);
if(tE>nE)
{
nE=tE;
nxt=tmp;
}
}
double dE=nE-E;
if(dE>0||exp(dE/step)>randNum())//找到更优或者符号概率去接受这个点
{
E=nE;
ans=nxt;
}
step*=0.9;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int T;
cin>>T;
while(T--)
{
scanf("%lf%lf%d",&X,&Y,&n);
for(int i=0;i<n;i++)
{
p[i].input();
}
Point text[30];
text[0]=Point(0,0),text[1]=Point(0,Y),text[2]=Point(X,0),text[3]=Point(X,Y);
Point ans=Point(X/2,Y/2);
double step=text[0].distance(text[3])/2.0;
Simulated(ans,step);
printf("The safest point is (%.1lf, %.1lf).\n",ans.x,ans.y);
}
return 0;
}