题解 P1337 【[JSOI2004]平衡点 / 吊打XXX】
Starlight237 · · 题解
这道题作为模拟退火的板子,缘于评价函数是一个单峰函数,故模拟退火AC率较高。而在相同时间内,粒子群算法的正确率是高于模拟退火的。
但是粒子群算法有一个缺点,在后期如果陷入了局部最优,就不容易调整。
所以我们可以将粒子群算法和模拟退火结合!这样粒子有一定概率跳出当前解,从而获得更优秀的全局搜索性。
注:因为这个算法跑得还是比较快的,可以多跑几遍pso增加正确率。
update 2019/7/23 20:51:粒子初始位置随机分布确实会增加AC率,更新了代码。
code:
#include<bits/stdc++.h>
using namespace std;
#define reg register
#define PSO_NUM 20
static int n,xx[1001],yy[1001],W[1001];
double T,w,bstans,gbx,gby,X1,Y1,r1,r2,f1,f2,dc;
struct obj{
double x,y,vx,vy,pbx,pby,bstans;
}obs[PSO_NUM|1];
inline double F(double x,double y){
double ans=0,dx,dy;
for(reg int i=1;i<=n;++i)
dx=x-xx[i],dy=y-yy[i],ans+=W[i]*sqrt(dx*dx+dy*dy);
return ans;
}
inline void pso(){
obs[1].x=gbx,obs[1].y=gby,obs[1].bstans=bstans;
for(reg int i=2;i<=PSO_NUM;++i)
obs[i].x=gbx+double((rand()<<1)-RAND_MAX)/RAND_MAX*10000,
obs[i].y=gby+double((rand()<<1)-RAND_MAX)/RAND_MAX*10000,
obs[i].bstans=F(obs[i].x,obs[i].y);
for(T=1000,w=0.7;T>1e-6;T*=0.92,w=max(0.0,w-0.005))
for(reg obj *i=obs+1,*j=obs+PSO_NUM+1;i!=j;++i)
r1=(double)rand()/RAND_MAX,r2=(double)rand()/RAND_MAX,
i->vx=w*i->vx+2*r1*(i->pbx-i->x)+2*r2*(gbx-i->x),
r1=(double)rand()/RAND_MAX,r2=(double)rand()/RAND_MAX,
i->vy=w*i->vy+2*r1*(i->pby-i->y)+2*r2*(gby-i->y),
i->x+=i->vx,i->y+=i->vy,
f1=F(i->x,i->y),
f1<i->bstans&&(i->bstans=f1,i->pbx=i->x,i->pby=i->y),
f1<bstans&&(bstans=f1,gbx=i->x,gby=i->y),
X1=i->x+double((rand()<<1)-RAND_MAX)/RAND_MAX*T,
Y1=i->y+double((rand()<<1)-RAND_MAX)/RAND_MAX*T,
f2=F(X1,Y1),
dc=f2-f1,
dc<0?i->x=X1,i->y=Y1:(
r1=(double)rand()/RAND_MAX,
r1<min(1.0,exp(-dc/T))&&(i->x=X1,i->y=Y1)
),
f2<i->bstans&&(i->bstans=f2,i->pbx=X1,i->pby=Y1),
f2<bstans&&(bstans=f2,gbx=X1,gby=Y1)
;
}
int main(){
srand(time(0));
srand(rand()^rand());
scanf("%d",&n);
for(reg int i=1;i<=n;++i)
scanf("%d%d%d",xx+i,yy+i,W+i),gbx+=xx[i],gby+=yy[i];
gbx/=n,gby/=n;
bstans=F(gbx,gby);
pso(),pso(),pso();
printf("%.3lf %.3lf",gbx,gby);
return 0;
}