模拟退火题单及代码

模拟退火题单

详解算法:https://www.cnblogs.com/linghusama/p/17716814.html

[TJOI2010] 分金币

https://www.luogu.com.cn/problem/P3878
先分类,然后任意交换其中两个币

#include<bits/stdc++.h>
#define int long long
using namespace std;
int w[35];
const int T0=5005;
const double lT=1e-14;
const double d=0.95;
int best,ans,nowans;int n;
int s1,s2,cnt1,cnt2;
int cal(){
	s1=0;
	s2=0;
	for(int i=1;i<=cnt1;i++){
		s1+=w[i];
	}
	for(int i=cnt1+1;i<=n;i++){
		s2+=w[i];
	}
	return abs(s1-s2);
}
void fire(){
	best=cal();
	int times=1000;
	while(times--){
		ans=best;
		for(double T=T0;T>=lT;T*=d){
			int le=rand()%cnt1+1;
			int ri=rand()%cnt2+1;
			ri+=cnt1;
			swap(w[le],w[ri]);
			nowans=cal();
			if(nowans<best){
				best=nowans;
			}
			
			if(nowans<ans||1.0*exp((ans-nowans)/T)>=1.0*rand()/RAND_MAX){
				ans=nowans;
			}
			else{
				swap(w[le],w[ri]);
			}
		}
	}
	cout<<best<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	int t;
	cin >> t;
	srand(time(NULL));
	while(t--){
		cin >> n;
		for(int i=1;i<=n;i++){
			cin >> w[i];
		}
		if(n==1){
			cout<<w[1]<<endl;
			continue;
		}
		cnt1=n/2;
		cnt2=n/2+(n%2);
		fire();
	}
} 
### RUNAWAY - Run Away

https://www.luogu.com.cn/problem/SP34
常见的二维任意点选取问题

#include<bits/stdc++.h>
using namespace std;
struct node{
	double x;
	double y;
}p[1005];
/*
求距离所有点的最小值最大 
*/
double dis(double  x,double y,double xx,double yy){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
int n;
double limitx,limity;
double cal(double x0,double y0){
	double ret=1e9;
	for(int i=1;i<=n;i++){
		ret=min(ret,dis(x0,y0,p[i].x,p[i].y));
	}
	return ret;//尝试x0,y0这个坐标下的答案 
}
//以上是对答案的计算部分
const int T0=10000;//初始温度 
const double d=0.98; //降温系数
const double Tl=1e-14;//最终降到的温度 
double ansx,ansy;//最终答案 
double RD(double T){
	return T*(rand()*2-RAND_MAX);
} 
void fire(){
	int times=10;//多做几次退火减少偶然性问题 
	double best=cal(ansx,ansy);//确定自己最初取得的较优解的答案 
	//注意这里best是写在times之外的,用于表示最终答案的最优值 
	while(times--){
		double ans=best;//ans再times循环内,表示是这一次退火的最终答案 
		
		double mayx=ansx;
		double mayy=ansy;
		for(double T=T0;T>=Tl;T*=d){//模拟降温过程 
			double xx=mayx+RD(T);
			double yy=mayy+RD(T);
			//尝试新的答案,新的答案由上一次答案随机得到.
			if(xx>limitx||yy>limity||xx<0||yy<0){
				continue;//不在范围内的可能性直接byebye 
			} 
			double nowans=cal(xx,yy);
			if(best<nowans){//如果答案更好,更新答案组成 (同于对比全局的最优值) 
				best=nowans;
				ansx=xx;
				ansy=yy;//全局的最优秀 
			} 
			if(ans<nowans||exp((ans-nowans)/T)>1.0*rand()/RAND_MAX){//寻找局部最优,要不然答案优秀直接更新,要不然随机化更新以跳出可能的局部最优解问题 
                ans=nowans;
				mayx=xx;
				mayy=yy;
            }

		}
	}
	
	
} 
//以上是模拟退火 
int main(){
	ios::sync_with_stdio(false);
	int t;
	cin >> t;
	srand(time(NULL));
	while(t--){
		
		
		cin >> limitx >> limity >> n;
		ansx=0;
		ansy=0;
		for(int i=1;i<=n;i++){
			cin >> p[i].x >> p[i].y;
			ansx+=p[i].x;
			ansy+=p[i].y;
		}
		ansx/=n;
		ansy/=n;//刚开始时建立一个初始答案,这里取得平均值。其实就是你感觉什么答案可能有点优秀就可以尝试把它当为初值。 
		
		//基本输入 
		fire();
		printf("The safest point is (%.1lf, %.1lf).\n",ansx,ansy);
		
	}
	
}

[JSOI2016] 炸弹攻击1

https://www.luogu.com.cn/problem/P5544
会打退火就乱做了

#include<bits/stdc++.h>
using namespace std;
struct bd{
	double x;
	double y;
	double r;
}building[15];
struct em{
	double x;
	double y;
}person[1005];
double dis(double x,double y,double xx,double yy){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
const double eps=1e-6;
int n,m;
double R;

int cal(double x,double y){
	//最大话半径肯定是较好的,就是说要和其他n相切取最小值 
	double r=1e9;
	for(int i=1;i<=n;i++){
		double diss=dis(x,y,building[i].x,building[i].y);
		diss-=building[i].r;
		r=min(r,diss);
	} 
	r=min(r,R);
	//然后记录圆的方程来康康在其内部的点有多少 
	int ret=0;
	for(int i=1;i<=m;i++){
		if(dis(x,y,person[i].x,person[i].y)<=r){
			ret++; 
		}
	}
	return ret;
}
const double T0=1e6;
const double d=0.98;
const double LT=1e-15; 
int best;
int ans;
double x00,y00; 
double RD(double T){
	return T*(rand()*2-RAND_MAX);
} 
void SA(){
	int times=100;
	best=cal(x00,y00); 
	while(times--){
		ans=best;
		double xx,yy;
		xx=x00;
		yy=y00;
		for(double T=T0;T>=LT;T*=d){
			double nowx=xx+RD(T);
			double nowy=yy+RD(T);
			int res=cal(nowx,nowy);
			if(res>best){
				best=res;
				x00=nowx;
				y00=nowy;
				xx=nowx;
				yy=nowy;
			}
			if(res>ans||exp((ans-res)/T)<=1.0*rand()/RAND_MAX){
				xx=nowx;
				yy=nowy;
				ans=res;
			}
			
		}
	}
	cout<<best;
}
int main(){
	ios::sync_with_stdio(false);
	srand(time(NULL));
	cin >> n >> m >> R;
	for(int i=1;i<=n;i++){
		cin >> building[i].x >> building[i].y >> building[i].r;
	}
	for(int i=1;i<=m;i++){
		cin >> person[i].x >> person[i].y;
	}
	x00=person[1].x;
	y00=person[1].y;
	SA();
	
}

「SPOJ4587」FENCE3 - Electric Fences

https://www.luogu.com.cn/problem/SP4587
还是简单呢。

#include<bits/stdc++.h>
using namespace std;
/*
对于一个点和任意一条线,有三种最短情况
一种是左端点
一种是右端点
一种是做垂线
*/
int n;
struct node{
	double lex;
	double ley;
	double rix;
	double riy;
}line[205];
double dis(double x,double y,double xx,double yy){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
double cal(double x,double y){
	double ret=0;
	for(int i=1;i<=n;i++){
		if(line[i].lex==line[i].rix){//竖着的 
			if(line[i].ley>line[i].riy){
				swap(line[i].ley,line[i].riy);
			}
			if(y>line[i].riy){
				ret+=dis(x,y,line[i].lex,line[i].riy);
			}
			else if(y<line[i].ley){
				ret+=dis(x,y,line[i].lex,line[i].ley);
			}
			else {
				ret+=fabs(x-line[i].lex);
			}
		}
		else{//横着的 
			if(line[i].lex>line[i].rix){
				swap(line[i].lex,line[i].rix);
			}
			if(x>line[i].rix){
				ret+=dis(x,y,line[i].rix,line[i].riy);
			}
			else if(x<line[i].lex){
				ret+=dis(x,y,line[i].lex,line[i].ley);
			}
			else {
				ret+=fabs(y-line[i].ley);
			}
		}
		
	} 
	return ret;
} 
//----------------------------------------
const double T0=1e5;
const double d=0.98;
const double LT=1e-15; 
double best;
double ans;
double x00,y00; 
double RD(double T){
	return T*(rand()*2-RAND_MAX);
} 
void SA(){
	int times=5;
	best=cal(x00,y00); 
	while(times--){
		ans=best;
		double xx,yy;
		xx=x00;
		yy=y00;
		for(double T=T0;T>=LT;T*=d){
			double nowx=xx+RD(T);
			double nowy=yy+RD(T);
			double res=cal(nowx,nowy);
			if(res<best){
				best=res;
				x00=nowx;
				y00=nowy;
				xx=nowx;
				yy=nowy;
			}
			if(res<ans||exp((ans-res)/T)>1.0*rand()/RAND_MAX){
				xx=nowx;
				yy=nowy;
				ans=res;
			}
			
		}
	}
	cout<<fixed<<setprecision(1);
	cout<<x00<<" "<<y00<<" "<<best;
}
int main(){
	ios::sync_with_stdio(false);
	srand(time(NULL));
	cin >>n;
	for(int i=1;i<=n;i++){
		cin >>line[i].lex >>line[i].ley>>line[i].rix>>line[i].riy; 
		x00+=line[i].lex;
		y00+=line[i].ley;
	}
//	cout<<cal(1.0,1.6)<<endl;
	x00/=n;
	y00/=n;
	SA();
	
}

锯木厂选址

dp?斜率优化?
爷!不!想!做!
模拟退火是哪两个就好啦

#include<bits/stdc++.h>
using namespace std;
#define int long long
int w[200005];
int d[200005];
int n;
int s;
//以上为基础量
int cal(int a,int b){
	return w[a]*d[a]+(w[b]-w[a])*d[b]+(w[n]-w[b])*d[n+1]-s;
}

int best;
int ans;
int a0;int b0;
int aa,bb;
const double T0=20005;
const double TL=1e-15;
const double D=0.98;
int RD(double T){
	return round(T*(rand()*2-RAND_MAX));
}
void SA(){
	best=cal(a0,b0);
	int times=500;
	while(times--){
		ans=best;
		aa=a0;
		bb=b0;
		for(double T=T0;T>=TL;T*=D){
			int nowa=aa+RD(T);
			int nowb=bb+RD(T);
			nowa=((nowa%n)+n)%n+1;
			nowb=((nowb%n)+n)%n+1;
			if(nowa==nowb){
				continue;
			}	
			if(nowa>nowb){
				swap(nowa,nowb);
			}
			int res=cal(nowa,nowb);
			if(res<best){
				best=res;
				a0=nowa;
				b0=nowb;
				aa=nowa;
				bb=nowb;
				ans=res;
			}
			if(res<ans||exp((ans-res)/T)>1.0*rand()/RAND_MAX){
				ans=res;
				aa=nowa;
				bb=nowb;
			}
			
		}
	}
	cout<<best<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	srand(time(NULL));
	cin >>n;
    w[0]=0;d[0]=0;s=0;
    for(int i=1;i<=n;i++)
    {
        int a,b;
        cin >>a >>b;
        w[i]=w[i-1]+a;
		d[i+1]=d[i]+b;
        s=s+a*d[i];
    }
	a0=1;
	b0=2;
	SA();
	
}

posted @ 2023-09-21 08:13  铃狐sama  阅读(33)  评论(0)    收藏  举报