模拟退火+爬山算法+思维

Posted 钟钟终

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟退火+爬山算法+思维相关的知识,希望对你有一定的参考价值。

CSDN话题挑战赛第2期

模拟退火算法:

归纳几点:
1.是一个随机算法,目的是取得最大值or最小值。首先需要选取初值,一般选为各个点的均值。
2.温度由高到低,每次的变化都伴随x值的跳跃选取
3.对于取最大值来说,若新值大于旧的值,则直接选用新值;若新值小于旧值则根据概率来决定是否跳跃。
4.一直到温度接近0度,停止运算。

P1337 [JSOI2004] 平衡点 / 吊打XXX

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=1e6+5;
const int inf=1e18;
const int mod=1e9+7;
int n;
struct node

    double x,y,w;
e[N];
double ansx,ansy,answ;
double energy(double x,double y) //能量总和越小越稳定 找最小值

    double r=0;
    for(int i=1;i<=n;i++)
    
        double dx=x-e[i].x;
        double dy=y-e[i].y;
        r+=sqrt(dx*dx+dy*dy)*e[i].w;
    
    return r;

void sa()

    double t=3000;
    while(t>1e-15)
    
        double ax=ansx+(rand()*2-RAND_MAX)*t; // [-32767,+32767]
        double ay=ansy+(rand()*2-RAND_MAX)*t;
        double aw=energy(ax,ay);    //随机跳值的x位置的y值
        double ad=aw-answ;
        if(ad<0)    //新值小于原值,接受
            ansx=ax,ansy=ay,answ=aw;
        else if(exp(-ad/t)*RAND_MAX>rand())        //新值大于原值,根据概率接受
        
            ansx=ax,ansy=ay;
        
        t*=down;
    

void solve()

    cin>>n;
    for(int i=1;i<=n;i++)
    
        cin>>e[i].x>>e[i].y>>e[i].w;

        ansx+=e[i].x,ansy+=e[i].y;
    
    ansx/=n,ansy/=n;    //均值作为初始值
    answ=energy(ansx,ansy);
    sa();sa();sa();sa();
    cout<<fixed<<setprecision(3)<<ansx<<" ";
    cout<<fixed<<setprecision(3)<<ansy<<endl;

signed main()

    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;


Problem D. Country Meow

各个点的能量总和越小越稳定,因此要枚举出最小点。

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=1e6+5;
const int inf=1e18;
const int mod=1e9+7;
int n;
struct node

    double x,y,z;
e[N];
double ansx,ansy,ansz,ans=inf;
double dis(node a1,node a2) //能量总和越小越稳定 找最小值

    return sqrt((a1.x-a2.x)*(a1.x-a2.x)+(a1.y-a2.y)*(a1.y-a2.y)+(a1.z-a2.z)*(a1.z-a2.z));

void sa()

    node k=ansx,ansy,ansz;
    double t=3000;
    while(t>1e-15)
    
        int g=1;
        for(int i=1;i<=n;i++)
        
            if(dis(k,e[i])>dis(k,e[g]))
                g=i;    //里当前点最远的那个点
        
        double d=dis(k,e[g]);
        ans=min(ans,d);
        /*k.x+=(e[g].x-k.x)/d*t;
        k.y+=(e[g].y-k.y)/d*t;
        k.z+=(e[g].z-k.z)/d*t;*/
        k.x+=(e[g].x-k.x)*(t/3000);//以一定概率靠近最远的那个点
        k.y+=(e[g].y-k.y)*(t/3000);
        k.z+=(e[g].z-k.z)*(t/3000);
        t*=down;
    

void solve()

    cin>>n;
    for(int i=1;i<=n;i++)
    
        cin>>e[i].x>>e[i].y>>e[i].z;
        ansx+=e[i].x,ansy+=e[i].y,ansy+=e[i].z;
    
    ansx/=n,ansy/=n,ansz/=n;    //均值作为初始值
    sa();sa();sa();sa();
    cout<<fixed<<setprecision(12)<<ans<<endl;

signed main()

    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;


P3878 [TJOI2010]分金币

思路:最多有30个金币。
用随机数种子对金币进行分类,重复1000次;再对每种分类交换A、B堆的一个金币,下标也是通过随机数得到,多次做差取最小值。

#include<bits/stdc++.h>
#define int long long
//#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=2e6+5;
const int inf=1e18;
const int mod=1e9+7;
int n,a[N],suma,sumb,ans;
vector<int>da,db;
void init()

    suma=sumb=0;
    da.clear(),db.clear();
    for(int i=1;i<=n;i++)
    
        if(rand()&1) da.push_back(a[i]);
        else db.push_back(a[i]);
    
    if(abs((int)da.size()-(int)db.size())>1)
    
        while(da.size()>db.size()+1)
        
            db.push_back(da.back());
            da.pop_back();
        
        while(db.size()>da.size()+1)
        
            da.push_back(db.back());
            db.pop_back();
        
    
    for(int x:da) suma+=x;
    for(int x:db) sumb+=x;
    ans=min(ans,abs(suma-sumb));
    //cout<<suma<<" "<<sumb<<endl;

void sa()

    init();
    for(int i=1;i<=1000;i++)
    
        int sa=suma,sb=sumb;
        int x=rand()%da.size(),y=rand()%db.size();
        sa=sa-da[x]+db[y];
        sb=sb-db[y]+da[x];
        if(abs(sa-sb)<ans)
        
            ans=abs(sa-sb);
            swap(da[x],db[y]);
            suma=sa,sumb=sb;
        
    

void solve()

    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    if(n==1)
    
        cout<<a[1]<<endl;return;
    
    ans=inf;
    for(int i=1;i<=1000;i++) sa();
    cout<<ans<<endl;

signed main()

    //ios;
    int T;cin>>T;
    while(T--)
        solve();
    return 0;


C.Removing Smallest Multiples

思路:枚举k值,用标记数组记录处理过的字符串中未出现的数。
不可以跳过一个k的倍数去处理另一个倍数,当时想到了这一点,不知道怎么编代码,真的好笨。。。

#include<bits/stdc++.h>
#define int long long
//#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=2e6+5;
const int inf=1e18;
const int mod=1e9+7;
int n,vis[N];
string s;
void solve()

    cin>>n>>s;
    s=" "+s;
    for(int i=1;i<=n;i++) vis[i]=0;
    int ans=0;
    for(int i=1;i<=n;i++)
    
        for(int j=i;j<=n;j+=i)
        
            if(s[j]=='1') break;
            if(vis[j]) continue;
            if(s[j]=='0'&&!vis[j])
            
                ans+=i;vis[j]=1;
            
        
    
    cout<<ans<<endl;

signed main()

    //ios;
    int T;cin>>T;
    while(T--)
        solve();
    return 0;


以上是关于模拟退火+爬山算法+思维的主要内容,如果未能解决你的问题,请参考以下文章

模拟退火算法

模拟退火

爬山算法和模拟退火算法简介

模拟退火

模拟退火算法

模拟退火算法和遗传算法