社区讨论
关于模拟退火中的 exp 函数的取值问题
P2179[NOI2012] 骑行川藏参与者 4已保存回复 7
讨论操作
快速查看讨论及其快照的属性,并进行相关操作。
- 当前回复
- 7 条
- 当前快照
- 1 份
- 快照标识符
- @lo917jui
- 此快照首次捕获于
- 2023/10/28 03:54 2 年前
- 此快照最后确认于
- 2023/10/28 03:54 2 年前
最开始我的判断是这么写的:
CPP if(res>=tmp)res=tmp;
else if(exp((ans-tmp)/T)<=1.0*rand()/RAND_MAX)sum[x]+=delta,sum[y]-=delta;
else res=tmp;
一直是 85 分,有几个点比答案大了一两百
然后我改成了这样:
CPP if(tmp<res||exp((ans-tmp)/T)>1.0*rand()/RAND_MAX)res=tmp;
else sum[x]+=delta,sum[y]-=delta;
就 95 分了,错的那个点还比答案小了点。
然后我把第一个
CPPif 里的两个条件顺序换了一下,就过了 if(exp((ans-tmp)/T)>1.0*rand()/RAND_MAX||tmp<res)res=tmp;
else sum[x]+=delta,sum[y]-=delta;
试了好多次了,都是一样的结果,应该不是偶然,但是这三种写法理论上应该是没有区别的,难道调用
exp 函数的位置不同还会影响取值?完整代码:
CPP#include<bits/stdc++.h>
using namespace std;
int n;
double tot;
double s[10005],k[10005],v[10005];
double sum[10005],lim[10005];
inline double Getv(int i){
return sqrt((sum[i]+lim[i])/k[i]/s[i])+v[i];
}
inline double Gett(int i){
return s[i]/Getv(i);
}
double ans,res;
inline void SA(){
for(double T=1;T>1e-18;T*=0.999992){
int x=rand()%n+1,y=rand()%n+1;
if(x==y)continue;
double delta=1.0*rand()/RAND_MAX*min(sum[x],T*1e14);
double tmp=res-Gett(x)-Gett(y);
sum[x]-=delta;sum[y]+=delta;
tmp+=Gett(x)+Gett(y);ans=min(ans,tmp);
if(exp((ans-tmp)/T)>1.0*rand()/RAND_MAX||tmp<res)res=tmp;
else sum[x]+=delta,sum[y]-=delta;
}
}
int main(){
scanf("%d%lf",&n,&tot);
for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&s[i],&k[i],&v[i]);
for(int i=1;i<=n;i++){
if(v[i]>=0)continue;
lim[i]=k[i]*v[i]*v[i]*s[i];tot-=lim[i];
}
for(int i=1;i<=n;i++)sum[i]=tot/n;
for(int i=1;i<=n;i++)ans+=Gett(i);
res=ans;
SA();
printf("%.10lf\n",ans);
return 0;
}
回复
共 7 条回复,欢迎继续交流。
正在加载回复...