2021.11.01 模拟退火

关于模拟退火的学习笔记 - 李有才99NL的博客 - 洛谷博客 (luogu.org)

模拟退火 - OI Wiki (oi-wiki.org)

题解 P1337 【[JSOI2004]平衡点 / 吊打XXX】 - SuperJvRuo 的博客 - 洛谷博客 (luogu.com.cn)

随机算法_模拟退火算法 - March On - 博客园 (cnblogs.com)

练习题:

P1337 [JSOI2004]平衡点 / 吊打XXX - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;

const int N=1010;
const double down=0.996;
int n;
double ansx,ansy,answ;
struct node{
	double x,y,w;
}a[N];

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline double energy(double x,double y){
	double fin=0;
	for(int i=1;i<=n;i++){
		double xi=x-a[i].x,yi=y-a[i].y;
		fin+=sqrt(xi*xi+yi*yi)*a[i].w;
	}
	return fin;
}
inline void simulate_anneal(){
	double t=3000;
	while(t>1e-14){
		double xi=ansx+(rand()*2-RAND_MAX)*t;
		double yi=ansy+(rand()*2-RAND_MAX)*t;
		double wi=energy(xi,yi);
		double cha=wi-answ;
		if(cha<0)ansx=xi,ansy=yi,answ=wi;
		else if(exp(-cha/t)*RAND_MAX>rand())ansx=xi,ansy=yi;
        // exp(x):e^x
		t*=down;
	}
}
inline void SA(){
	simulate_anneal();simulate_anneal();simulate_anneal();
}

int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i].x=read(),a[i].y=read(),a[i].w=read();
		ansx+=a[i].x;ansy+=a[i].y;
	}
	ansx/=n;ansy/=n;answ=energy(ansx,ansy);
	SA();
	printf("%.3lf %.3lf",ansx,ansy);
	return 0;
}

P2210 Haywire - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

//随机一个数列,不断计算答案,交换元素,得到最优解 
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;

const double down=0.996;
const double MAX_TIME=0.996;
const int N=20;
int n,a[N][4],pos[N];
int ans=0x7fffffff; 

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline int energy(){
	int fin=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=3;j++)
	fin+=abs(pos[i]-pos[a[i][j]]);
	return fin/2;
}
inline void simulate_anneal(){
	double t=3000;
	while(t>1e-15){
		int x=rand()%n+1,y=rand()%n+1;
		swap(pos[x],pos[y]);
		int ansi=energy();
		int cha=ansi-ans;
		if(cha<0)ans=ansi;
		else if(exp(-cha/t)*RAND_MAX<=rand())swap(pos[x],pos[y]);
		t*=down;
	}
}
inline void SA(){
	while((double)clock()/CLOCKS_PER_SEC<MAX_TIME)simulate_anneal();
	//卡时专用,但是容易写挂,oi wiki上说“这里的 MAX_TIME 是一个自定义的略小于时限的数”,到底是多少?
}

int main(){
	n=read();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=3;j++)a[i][j]=read();
		pos[i]=i;
	}
	ans=energy();
	SA();
	cout<<ans;
	return 0;
}

[P2503 HAOI2006]均分数据 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

注:忽然明白模拟退火就是一种模拟,十分暴力和随机,根本不讲究算法

// 随机分组?? 
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<cstring>
using namespace std;

const int N=25;
const double down=0.996;
const double MAX_TIME=0.99;
int n,m,belong[N];
double tot[N],a[N];
double ans=0x3f3f3f3f,average;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline double energy(){
	double fin=0;
	for(int i=1;i<=m;i++)fin+=(tot[i]-average)*(tot[i]-average);
	fin/=(double)m;//fin=sqrt(fin);
	return fin;
}
inline void change(int x,int to){
	tot[belong[x]]-=a[x];
	tot[to]+=a[x];
	belong[x]=to;
}
inline void simulate_anneal(){
	double t=3000,ansii=ans;
	while(t>1e-15){
		int x=rand()%n+1,y=rand()%m+1;
		int yi=belong[x];
		change(x,y);
		double ansi=energy();
		double cha=ansi-ans;
		if(cha<0){
			ans=ansi;
			//if(ans>ansii)ans=ansii;
		}
		else if(exp(-cha/t)*RAND_MAX<=rand())change(x,yi);
		t*=down;
	}
}
inline void SA(){
	while((double)clock()/CLOCKS_PER_SEC<MAX_TIME)simulate_anneal();
}

int main(){
	srand(time(0));
	n=read();m=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		average+=a[i];
		int x=rand()%m+1;
		belong[i]=x;
		tot[x]+=a[i];
	}
	average/=m;
	ans=energy();
	SA();
	printf("%.2lf",(double)sqrt(ans));
	return 0;
}

[P5544 JSOI2016]炸弹攻击1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<ctime>
using namespace std;

const int N=1e3+10;
const double down=0.996;
const double MAX_TIME=0.96;
int n,m,r;
double ansx,ansy;
int ans;
struct node{
	double x,y,r;
}build[15];
struct nodei{
	double x,y;
}enemy[N];

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline double calc(double x,double y,double u,double v){
	return (double)(sqrt((x-u)*(x-u)+(y-v)*(y-v)));
}
int energy(double x,double y){
	int fin=0;
	double dis=r;
	for(int i=1;i<=n;i++)dis=min(dis,calc(x,y,build[i].x,build[i].y)-build[i].r);
	for(int i=1;i<=m;i++)if(dis>=calc(x,y,enemy[i].x,enemy[i].y))++fin;
	return fin;
}
inline void simulate_anneal(){
	double t=3000;
	while(t>1e-15){
		double x=ansx+(rand()*2-RAND_MAX)*t;
		double y=ansy+(rand()*2-RAND_MAX)*t;
		int ansi=energy(x,y);
		int cha=ansi-ans;
		if(cha>0)ansx=x,ansy=y,ans=ansi;
		else if(exp(-cha/t)*RAND_MAX<rand())ansx=x,ansy=y;
        //这里与别的不同,因为这里要求ans越大越好
		t*=down;
	}
	///cout<<ans<<endl;
}
inline void SA(){
	while((double)clock()/CLOCKS_PER_SEC<MAX_TIME)simulate_anneal();
}

int main(){
	n=read();m=read();r=read();
	for(int i=1;i<=n;i++)
	build[i].x=(double)read(),build[i].y=(double)read(),build[i].r=(double)read();
	for(int i=1;i<=m;i++)
	enemy[i].x=(double)read(),enemy[i].y=(double)read(),ansx+=enemy[i].x,ansy+=enemy[i].y;
	ansx/=(double)m,ansy/=(double)m;
	ans=energy(ansx,ansy);
	//cout<<ans<<endl;
	SA();
	cout<<ans;
	return 0;
}

[P3878 TJOI2010]分金币 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

30分TLE代码

// 我寻思着又是一个随机分组,但是二进制遍历似乎也可以
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<cstring>
using namespace std;

const int N=30;
int t,n,a[N],belong[N],sum[4],ans=0x3f3f3f3f;
const double down=0.9112;

inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*w;
}
inline int energy(){
	int sum1=0,sum2=0;
	for(int i=1;i<=(n+1)/2;i++)sum1+=a[i];
	for(int i=(n+1)/2+1;i<=n;i++)sum2+=a[i];
	return abs(sum1-sum2);
}
inline void simulate_anneal(){
	double t=5000;
	while(t>1e-10){
		int x=rand()%n+1,y=rand()%n+1;
		swap(a[x],a[y]);
		int ansi=energy();
		int cha=ansi-ans;
		if(cha<0)ans=ansi;
		else if(exp(-cha/t)*RAND_MAX<=rand())swap(a[x],a[y]);
		t*=down;
	}
}
inline void SA(){
	for(int i=1;i<=1000;i++)simulate_anneal();
}

int main(){
	t=read();
	while(t--){
		memset(a,0,sizeof(a));
		n=read();
		if(n==1){
			cout<<"0"<<endl;
			continue;
		}
		for(int i=1;i<=n;i++)a[i]=read();
		ans=energy();
		SA();
		cout<<ans<<endl;
	}
	return 0;
}

别人ac代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define inf 2147483647
#define re register
using namespace std;
int n,ans=inf,a[1005];
int get()
{
    int sum1=0,sum2=0;
    for (re int i=1;i<=(n+1)/2;i++)
     	sum1+=a[i];
    for (re int i=(n+1)/2+1;i<=n;i++)
     	sum2+=a[i];
    return abs(sum1-sum2);
}
void SA()
{
    double beginT=5000,endT=1e-10,changeT=0.9112;
    for (re double T=beginT;T>endT;T*=changeT)
    {
       	int x=rand()%n+1, y=rand()%n+1;
        swap(a[x],a[y]);
        int sum=get();
        if (sum<ans)
        	 ans=sum;
        else 
            if (exp((ans-sum)/T)<(double(rand())/RAND_MAX))
         		swap(a[x],a[y]);
    }
}
int main()
{
    srand(rand());
    int T;
    cin>>T;
    while (T--)
    {
        cin>>n;
    	for(int i=1;i<=n;i++)
     		cin>>a[i];
        int ctrl=1000;
        while(ctrl--)
        	SA();
        cout<<ans<<endl;
        ans=inf;
    }
} 
 posted on 2021-11-01 17:15  eleveni  阅读(49)  评论(0)    收藏  举报