社区讨论

关于模拟退火中的 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 分了,错的那个点还比答案小了点。
然后我把第一个 if 里的两个条件顺序换了一下,就过了
CPP
		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 条回复,欢迎继续交流。

正在加载回复...