https://vjudge.net/problem/UVA-10228
模拟退火平面坐标系中的板题
注意使用rand()而不是mt19937 rnd(),因为rand()范围比较小,比较适合做这种坐标范围比较小的题,用mt19937*当前温度T加进x和y中就会变化非常大,很难得到最优解,rand()在windows中范围是32767,非常合适.
upd:可以使用dis(rnd)来修改范围,就能过了。根据不同的题修改不同的范围,还能返回实数。
模拟退火过不去的时候,可以增大初始温度,增大delta的值,增加模拟退火次数,减小停止温度等方法
#include<bits/stdc++.h>
using namespace std;
mt19937 rnd(time(0));
uniform_int_distribution<> dis(1,100000);
const double delta=0.996;
const int maxl=110;
int n,cas;
struct point
{
double x,y;
}a[maxl];
double ansx,ansy,ans,t;
int tx[9]={0,1,0,-1,0,1,1,-1,-1};
int ty[9]={0,0,1,0,-1,1,-1,1,-1};
inline void prework()
{
scanf("%d",&n);
double sumx=0,sumy=0;
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&a[i].x,&a[i].y);
sumx+=a[i].x;sumy+=a[i].y;
}
ansx=sumx/n;ansy=sumy/n;
}
inline double calc(double x,double y)
{
double res=0,dx,dy;
for(int i=1;i<=n;i++)
{
dx=x-a[i].x;dy=y-a[i].y;
res+=sqrt(dx*dx+dy*dy);
}
return res;
}
inline void anneal()
{
double x=ansx,y=ansy,nx,ny;
t=3000;int f;
while(t>1e-20)
{
f=1;if(dis(rnd)&1) f=-1;
nx=x+(f*dis(rnd))*t;
f=1;if(dis(rnd)&1) f=-1;
ny=y+(f*dis(rnd))*t;
double now=calc(nx,ny);
if(now<ans)
{
ansx=nx;ansy=ny;
x=nx;y=ny;
ans=now;
}
else if(exp(-(now-ans)/t)>1.0*dis(rnd)/100000)
x=nx,y=ny;
t*=delta;
}
}
inline void mainwork()
{
ans=calc(ansx,ansy);
anneal();
anneal();
anneal();
}
inline void print()
{
long long d=ans;
if(ans-d>=0.5)
d++;
printf("%lld\n",d);
}
int main()
{
srand(time(0));
int t;
scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
prework();
mainwork();
print();
if(cas!=t)
puts("");
}
return 0;
}