模拟退火模板(SA)

要取得[a,b)的随机整数,使用(rand() % (b-a))+ a;
要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a;
要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1;
通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。
要取得0~1之间的浮点数,可以使用rand() / double(RAND_MAX)。

1e-1精度:下界1e-3或1e-4 降温系数0.8

1e-5精度:下界1e-7或1e-8 降温系数0.99

洛谷https://www.luogu.org/problem/P2210

#include<bits/stdc++.h>
using namespace std;
namespace IO {
    struct q_instream {
        template < typename classT >
        inline q_instream operator>>(classT &e) const {
            e= 0;
            classT f= 1, c= 0;
            while(c < '0' || c > '9') {
                if(c == '-') f= -1;
                c= getchar();
            }
            while(c >= '0' && c <= '9') e= e * 10 + c - '0', c= getchar();
            return e= e * f, (*this);
        }
    } in;
    //读入优化
    struct q_outstream {
        template < typename classT >
        inline q_outstream operator<<(const classT &e) const {
            if(e < 0) {
                putchar('-');
                (*this) << -e;
            }
            else {
                if(e > 9) (*this) << (e / 10);
                putchar(e % 10 + '0');
            }
            return (*this);
        }
        inline q_outstream operator<<(const char &c) const {
            return putchar(c), (*this);
        }
    } out;
    //输出优化
}
using namespace IO;

int n;
int mp[13][4];
int pos[13];

long long get_cost(){//因题而异 
	long long ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=3;j++){
			ans+=abs(pos[i]-pos[mp[i][j]]);
		}
	}
	return ans;
}

//SA模板 
int best_ans=0x3f3f3f3f;
const double BeginT= 10000, EndT= 1e-14, ChangeT= 0.99;
//起始温度,终止温度,温度改变速度
void SA(int times) {
//模拟退火过程
    long long x, y, tmp_ans;
    while(times--) {
        for(double T= BeginT; T > EndT; T*= ChangeT) {
            do {
                x= rand() % n + 1;//随机产生两个不相同的点 
                y= rand() % n + 1;
            } while(x == y);
            
            swap(pos[x], pos[y]);//任意交换两个牛的位置
            tmp_ans= get_cost();//求出当前解
            
            if(tmp_ans <= best_ans) {//更优就更新答案
                best_ans= tmp_ans;
            }
            else if(exp((best_ans - tmp_ans) / T) > (double)rand() / RAND_MAX) {//条不接收较差的条件
                swap(pos[x], pos[y]);//不接受较差解,将两个牛位置交换回去
            }
        }
    }
    return;
}

int main(){
	srand(rand()); srand(rand()); //玄学srand
	
	in>>n;
	for(int i=1;i<=n;i++){
		pos[i]=i;
		for(int j=1;j<=3;j++){
			in>>mp[i][j];
		}
	}
	
	SA(200);
	printf("%d\n",best_ans/2);
	
	return 0;
} 

洛谷https://www.luogu.org/problem/P1337

#include<bits/stdc++.h>
using namespace std;

int n;
struct node{
	double x,y,w;
};
vector<node> vec;

double get_cost(double cx,double cy){
	double sum=0;
	for(int i=0;i<n;i++){
		double x,y,w;
		x=vec[i].x-cx;y=vec[i].y-cy;w=vec[i].w;
		
		sum+=sqrt(x*x+y*y)*w;
	}
	return sum;
}

double ansx,ansy;
//SA模板 
double best_ans=1e18+7;
const double BeginT= 10000, EndT= 1e-15, ChangeT= 0.996;
//			起始温度,       终止温度,    温度改变速度W
void SA(int times) {
//模拟退火过程
    double x, y, tmp_ans;
    while(times--) {
        for(double T= BeginT; T > EndT; T*= ChangeT) {
            x=ansx+(rand()*2-RAND_MAX)*T;//rand()*2-RAND_MAX的范围是 
            y=ansy+(rand()*2-RAND_MAX)*T;//-RAND_MAX到RAND_MAX 
            
            tmp_ans=get_cost(x,y);
            
            if(tmp_ans < best_ans) {//更优就更新答案
                best_ans= tmp_ans;
                ansx=x;
                ansy=y;
            }
            else if(exp((best_ans - tmp_ans) / T) > (double)rand() / RAND_MAX) {//否则根据多项式概率接受
                ansx=x;
                ansy=y;
            }
        }
    }
    return;
}

int main(){
	srand(rand()); srand(rand()); //玄学srand
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		double x,y,z;
		scanf("%lf%lf%lf",&x,&y,&z);
		vec.push_back(node{x,y,z});
		ansx+=x;ansy+=y;
	}
	
	SA(6);
	printf("%.3lf %.3lf\n",ansx,ansy);
	
	return 0;
} 

HDUhttp://acm.hdu.edu.cn/showproblem.php?pid=2899

#include<bits/stdc++.h>
using namespace std;

double get_cost(double x,double y){
	return 6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*pow(x,2)-y*x;
}
double y;

//SA模板 
double best_ans=1e18+7;
const double BeginT= 10000, EndT= 1e-15, ChangeT= 0.98;
//起始温度,终止温度,温度改变速度
void SA(int times) {
	best_ans=1e18+7;
//模拟退火过程
    double x, tmp_ans;
    while(times--) {
        for(double T= BeginT; T > EndT; T*= ChangeT) {
        	for(int i=1;i<=2;i++){
        		double tx;
        		if(i==1){
        			tx=x+T;
				}
				else tx=x-T;
				if(tx>100||tx<0)continue;
				
				tmp_ans=get_cost(tx,y);
            
	            if(tmp_ans <= best_ans) {//更优就更新答案
	                best_ans= tmp_ans;
	                x=tx;
	            }
	            else if(exp((best_ans - tmp_ans) / T) > (double)rand() / RAND_MAX) {//条不接收较差的条件
	                continue;
	            }
			}
        }
    }
    return;
}

int main(){
	srand(rand());
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf",&y);
		SA(5);
		printf("%.4lf\n",best_ans);
	}
	
	return 0;
}

二维坐标

HDUhttp://acm.hdu.edu.cn/showproblem.php?pid=3932

#include<bits/stdc++.h>
using namespace std;

#define PI (3.14159265358979323846)
#define PI_DIV_180 (0.017453292519943296)//π/180
#define DegToRad(x)    ((x)*PI_DIV_180)//角度转换为弧度

int nxt[8][2]={{0,1},{0,-1},{1,0},{-1,0},{1,1},{-1,-1},{1,-1},{-1,1}};
double X,Y;
int n;

struct node{
    double x,y;
};
vector<node> vec;

double get_cost(double x,double y){
    double ans=0;
    for(int i=0;i<n;i++){
        double tx,ty;
        tx=x-vec[i].x;
        ty=y-vec[i].y;
        ans=max(ans,sqrt(tx*tx+ty*ty));
    }
    return ans;
}

double ansx,ansy;
//SA模板 
double best_ans=1e18+7;
const double BeginT= 10000, EndT= 1e-8, ChangeT= 0.993;
//起始温度,终止温度,温度改变速度
void SA(int times) {
    best_ans=1e18+7;
//模拟退火过程
    double x,y, tmp_ans;
    x=X;y=Y;
    while(times--) {
        for(double T= BeginT; T > EndT; T*= ChangeT) {
            double tx,ty;
            for(int i=0;i<8;i++){
                tx=x+(double)(rand()%1000+1)/1000.0*nxt[i][0]*T;
                ty=y+(double)(rand()%1000+1)/1000.0*nxt[i][1]*T;
                
                if(tx>X||ty>Y||tx<0||ty<0)continue;
                
                tmp_ans=get_cost(tx,ty);
                
                if(tmp_ans < best_ans) {//更优就更新答案
                    best_ans= tmp_ans;
                    x=tx;y=ty;
                    ansx=tx;ansy=ty;
                }
                else if(exp((best_ans - tmp_ans) / T) > (double)rand() / RAND_MAX) {//条不接收较差的条件
                    //x=tx;y=ty;
                }
            }
        }
    }
    return;
}

int main(){
    srand(rand());
    
    while(~scanf("%lf%lf%d",&X,&Y,&n)){
        vec.clear();
        
        for(int i=1;i<=n;i++){
            double a,b;
            scanf("%lf%lf",&a,&b);
            vec.push_back(node{a,b});
        }
        
        SA(6);
        printf("(%.1lf,%.1lf).\n%.1lf\n",ansx,ansy,best_ans);
    }
    
    return 0;
}

大佬博客:

https://www.luogu.org/blog/m-sea/qian-tan-SA

https://www.luogu.org/blog/Ciyang/solution-p2210

https://www.cnblogs.com/flashhu/p/8884132.html

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值