Run Away HDU - 1109(模拟退火法)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值