题解 P1337 【[JSOI2004]平衡点 / 吊打XXX】(模拟退火总结)
胡扯
题面
本题的正解是———
是什么呢?
我也不知道
但这确实是道入手模拟膜你退火的好题
原题链接:here
首先澄清一下,模拟退火不是正解!不是正解!不是正解!
ta是用来骗分的,而且这道题能骗到满分
辣鸡膜你退火毁我青春↑(前面还有一排89分。。。)
为什么基本一样的程序会得分不同呢?所以先看膜你退火的概念
概念
首先了解金属退火(or you won't understand 'temperature')
说人话就是高温向低温冷却,同时瞎跳,温度越高跳率越大,跳的越远,最后停在某高峰如下图所示↓
设xs为降温系数('X'i 'S'hu)每次温度乘上xs(xs一般略小于1)
当温度小于一个常数(大于0)时终止
中间需要引入随机数
如果当前跳到的值优于最优解,则正式跳到这来(下一步从这里跳)
否则以一定概率跳到那里(最好结合当前温度)
因为是随机算法,所以可以肆无忌惮地瞎搞比如多跑几遍%你退火
现在知道该算法为什么是rp算法了吧
调参
然后模拟退火最玄学的地方就浮出水面了
既然拼rp,总要有个下限保证正确率吧
所以我们调整参数
增加正确率的办法
-
调大降温系数(但还是要小于1,个人推荐)
-
增高初始温度(个人没用ta产生过质变)
-
减小结束温度(但还是要大于0,个人同样没用ta产生过质变)
-
多跑几遍SA(即模拟退火)
-
QwQ
不过相应时间复杂度会提高,所以
道路千万条,防TLE第一条,不想卡时限,TLE两行泪
卡时限方法:( MAXTIME 系寄几定义的变量,一般0.7-0.9)
while ((double)clock()/CLOCKS_PER_SEC<MAXTIME)
SA();
实在是AC不了也可以更换随机数种子
甚至直接:
srand(time(NULL));//srand(xxxxx);srand(rand());
提问
Q:如果您都试过了,还是AC不了,怎么办?
A:做题前洗脸洗手,扶老奶奶过马路,rp++
Q:如果您还是AC不了,怎么办?
A:那您还是打正解吧
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int x[1010],y[1010],w[1010];
double ansx,ansy,ans=1000000000001,temperature;
double xs=0.995;
int tmpx,tmpy;
double calc(double x0,double y0)//计算器
{
double res=0,dx,dy;
for(int i=1;i<=n;++i)
{
dx=x[i]-x0;dy=y[i]-y0;
res+=sqrt(dx*dx+dy*dy)*w[i];
}
return res;
}
int SA()//模拟退火
{
double tx=ansx,ty=ansy;
temperature=10000;
while(temperature>0.0000000000001)
{
double X=ansx+temperature*(rand()*2-RAND_MAX);
double Y=ansy+temperature*(rand()*2-RAND_MAX);//一个可负可正可零的随机数
double now=calc(X,Y)-ans;
if(now<0)
{
tx=X;
ty=Y;
ans=calc(X,Y);
ansx=X;
ansy=Y;
}
else
{
if(exp(-now/temperature)*RAND_MAX>rand())
{
tx=X,ty=Y;
}
}
temperature*=xs;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>x[i]>>y[i]>>w[i];
tmpx+=x[i];
tmpy+=y[i];
}
srand(time(NULL));//玄学种子
ansx=(double)tmpx/n;
ansy=(double)tmpy/n;//据说从平均值开始会快一些?
SA();
SA();
SA();//多跑几遍SA提正确率
printf("%.3lf %.3lf\n",ansx,ansy);
}
基础讲完了就讲模拟退火套其他算法
P2503 [HAOI2006]均分数据
TA的题解
其实跳不出模拟退火板子,只是套用或混用