具体模拟退火的原理可参考 http://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html
模拟退火可用于一些 精度 要求不是很高的题目。
比如取答案的0.1,或者没有小数点
poj2420:
/*
题意:给定n个点,找到一个点,使得n个点到这个点的距离和最小
模拟退火法
模拟退火的过程
1 确定生成点的范围,初设为矩形,在这个范围内生成NUM个点(NUM自定)
2 确定最高温度step,以及退温系数,接下来进行退火
3 对于每个生成的点i,在以其为原点,半径为step的!圆周上!,随机生成k个点(注意踢掉不符合的点),一旦有点优于i,则替换。
(由于step的不断缩小,所以总会找到符合的点)
4 缩小范围D,若D<精度,退出,否则执行
5 遍历所有NUM个点,找到val的最大值
*/
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <cstring>
using namespace std;
#define INF 1e20
const double pi=acos(-1.0);
const int maxn=1010;
const int num=20;
struct point
{
double x,y;
point(){}
point(double a,double b){x=a,y=b;}
}p[maxn],may[num+10];//原点,随机生成的点
int n;
double val[num+10]; //价值数组
double maxx,maxy;
double minx,miny;
double Rand() //随机生成0---1
{
return double(rand()%1000+1)/1000.000;
}
double dist(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double judge(point tmp) //判断函数
{
double len=0;
for(int i=1;i<=n;i++)
len+=dist(tmp,p[i]);
return len;
}
void init()//第一步 生成num个解(包括读入啦)
{
for(int i=1;i<=num;i++)
{
may[i].x=minx+(maxx-minx)*Rand();
may[i].y=miny+(maxy-miny)*Rand();
val[i]=judge(may[i]);
}
}
void solve()
{
init(); //第一步 生成num个解
double d,step=sqrt((maxx-minx)*(maxx-minx)+(maxy-miny)*(maxy-miny)); //第二步,确定最高温度 ,最糟糕的step为对角线
while(step>0.001)
{
for(int i=1;i<=num;i++)//第三步,针对每个点
{
for(int j=1;j<=30;j++) //再随机生成若干个解
{
d=Rand()*2*pi; //
point tmp;
tmp.x=may[i].x+step*cos(d);
tmp.y=may[i].y+step*sin(d); //在may[i]在原点,半径为step的圆周上随机生成点
if(tmp.x<minx||tmp.x>maxx||tmp.y<miny||tmp.y>maxy) //点在符合的范围内
continue;
double temp=judge(tmp);
if(temp<val[i]) //优 则替换
val[i]=temp,may[i]=tmp;
}
}
step*=0.85; //第四步,退火
}
int idx=1; //遍历,寻找最优解
for(int i=2;i<=num;i++)
if(val[i]<val[idx])
idx=i;
printf("%.0lf\n",val[idx]);
}
int main()
{
srand((unsigned)time(NULL)); //必须的语句,
while(scanf("%d",&n)!=EOF)
{
maxx=-INF,maxy-INF;
minx=INF,miny=INF;
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
maxx=max(maxx,p[i].x);
maxy=max(maxy,p[i].y);
minx=min(minx,p[i].x);
miny=min(miny,p[i].y);
}
solve();
}
}
/*
题意:给定n个点和范围(0,0)--(x,y),在范围内找到一个点,使得n个点到这个点的最小距离最大
模拟退火法
模拟退火的过程
1 确定生成点的范围,在这个范围内生成NUM个点(NUM自定)
2 确定最高温度step,以及退温系数,接下来进行退火
3 对于每个生成的点i,在以其为原点,半径为step的!圆周上!,随机生成k个点(注意踢掉不符合的点),一旦有点优于i,则替换。
(由于step的不断缩小,所以总会找到符合的点)
4 缩小范围D,若D<精度,退出,否则执行
5 遍历所有NUM个点,找到val的最大值
*/
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <cstring>
using namespace std;
const double pi=acos(-1.0);
const int maxn=1010;
const int num=20;
struct point
{
double x,y;
point(){}
point(double a,double b){x=a,y=b;}
}p[maxn],may[num+10];//原点,随机生成的点
int n;
double val[num+10]; //价值数组
double x,y;
double Rand() //随机生成0---1
{
return double(rand()%1000+1)/1000.000;
}
double dist(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double judge(point tmp) //判断函数
{
double len=10000000000.0;
for(int i=1;i<=n;i++)
len=min(len,dist(tmp,p[i]));
return len;
}
void init()//第一步 生成num个解(包括读入啦)
{
scanf("%lf%lf%d",&x,&y,&n);
for(int i=1;i<=n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
for(int i=1;i<=num;i++)
{
may[i].x=x*Rand();
may[i].y=y*Rand();
val[i]=judge(may[i]);
}
}
void solve()
{
srand((unsigned)time(NULL)); //必须的语句,
init(); //第一步 生成num个解
double d,step=sqrt(x*x+y*y); //确定最高温度 ,最糟糕的step为对角线
while(step>0.001)
{
for(int i=1;i<=num;i++)
{
for(int j=1;j<=30;j++) //再随机生成若干个解
{
d=Rand()*2*pi; //
point tmp;
tmp.x=may[i].x+step*cos(d);
tmp.y=may[i].y+step*sin(d); //在may[i]在原点,半径为step的圆周上随机生成点
if(tmp.x<0||tmp.x>x||tmp.y<0||tmp.y>y)
continue;
double temp=judge(tmp);
if(temp>val[i]) //优 则替换
val[i]=temp,may[i]=tmp;
}
}
step*=0.85; //退火
}
int idx=1;
for(int i=2;i<=num;i++)
if(val[i]>val[idx])
idx=i;
printf("The safest point is (%.1lf, %.1lf).\n",may[idx].x,may[idx].y);
}
int main()
{
int cas;
scanf("%d",&cas);
while(cas--)
solve();
}