zoukankan      html  css  js  c++  java
  • [学习笔记]模拟退火

    浅谈玄学算法——模拟退火

    玄学,没有什么可说的。。。

    delta一般是0.99多

    初始温度T几千?

    终止温度1e-10左右

    SA次数大概6次

    记住板子:

    1.坐标的选择范围和温度有关

    2.不优解的取舍和温度有关

    3.只有取到更优解的时候才更新ans

    4.取舍判断条件:

    if(Delta<0){
        //upda ans
        //upda now
    }
    else if(exp(-Delta/T)*RAND_MAX>rand()) //upda now

    这个exp(-Delta/T)是0~1之间的小数,Delta越小、T越大,值越大

    IOI赛制多交几次就好了

    例题: [JSOI2004]平衡点 / 吊打XXX 

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define il inline
    #define reg register int
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=1000+5;
    const double deta=0.995;
    int n;
    struct po{
        double x,y;
        double w;
        po(){}
        po(double xx,double yy){
            x=xx;y=yy;w=0.0;
        }
    }a[N];
    double dis(po a,po b){
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    double calc(double x,double y){
        double ret=0;
        for(reg i=1;i<=n;++i){
            ret+=dis(a[i],po(x,y))*a[i].w;
        }
        return ret;
    }
    double ans;
    double sx,sy;
    double ax,ay;
    void SA(){
        double x=sx,y=sy;
        double T=2000;
        while(T>1e-14){
            double X=x+((rand()<<1)-RAND_MAX)*T;
            double Y=y+((rand()<<1)-RAND_MAX)*T;
            double tmp=calc(X,Y);
            double cha=tmp-ans;
            if(cha<0){
                ans=tmp;
                ax=X;ay=Y;
                x=X;y=Y;
            }
            else if(exp(-cha/T)*RAND_MAX>rand()) x=X,y=Y;
            T*=deta;
        }
    }
    int main(){
        srand(19260817);
        rd(n);
        for(reg i=1;i<=n;++i){
            scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].w);
            sx+=a[i].x,sy+=a[i].y;
        }
        ans=1e18;
        sx/=n;sy/=n;
        for(reg i=1;i<=10;++i){
            SA();
        }
        printf("%.3lf %.3lf",ax,ay);
        return 0;
    }    
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/1/20 19:21:02
    */

    [HAOI2006]均分数据

    这个就是和DP结合

    排列方式或者决策点可能不是最优的,但是calc得到的答案必须是精确的

    所以要精确DP

    这个是序列的SA

    直接交换。

    不行再换回来

    至于最后为什么还要交换,可能考虑到总是可能差一点得到最优解吧。。。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=22;
    const int M=8;
    const int inf=0x3f3f3f3f;
    const double eps=1e-10;
    int ans,a[N];
    int f[N][M];
    int n,m;
    int s[N];
    double delta=0.99;
    int dp(){
        memset(f,0x3f,sizeof f);
        memset(s,0,sizeof s);
        f[0][0]=0;
        for(int i=1;i<=n;i++){
          s[i]=s[i-1]+a[i];
          for(int k=1;k<=min(m,i);k++){
              for(int j=0;j<i;j++){
                  f[i][k]=min(f[i][k],f[j][k-1]+(s[i]-s[j])*(s[i]-s[j]));
            }
          }
        }
        return f[n][m];
    }
    void SA(){
        double T=2000;
        int haha=0;
        while(T>1e-14){
            //cout<<T<<endl;
            int x=0,y=0;
            while(x==y) x=rand()%n+1,y=rand()%n+1;
            swap(a[x],a[y]);
            int now=dp();
            int d=now-ans;
            if(d<0) ans=now;
            else if(exp((double)-d/T)*RAND_MAX>rand()) haha++;
            else swap(a[x],a[y]);
            T*=delta;
        }
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                swap(a[i],a[j]);
                ans=min(ans,dp());
                swap(a[i],a[j]);
            }
        }
    }
    void sol(){
        ans=inf;
        while((double)clock()/CLOCKS_PER_SEC<0.8) SA();
    }
    int main(){
        srand(19260817);
        scanf("%d%d",&n,&m);
        int sum=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        sol();
        
        double ave=(double)sum/(double)m;
        double up=(double)ans-2*sum*ave+m*ave*ave;
        //cout<<ans<<" "<<ave<<" "<<up<<endl;
        if(up/((double)m)<eps) printf("0.00");
        else printf("%.2lf",sqrt(up/(double)m));
        return 0;
    }
  • 相关阅读:
    数据仓库基本概念
    收藏--关于命名规范、维度明细层及集市汇总层设计的思考
    Thinkphp6框架学习:有关数据库的基本操作
    算法第一章作业
    解决 Intellij IDEA Cannot Resolve Symbol ‘BASE Decoder’ 问题
    利用Kruskal算法求最小生成树解决聪明的猴子问题 -- 数据结构
    利用BFS解决拯救007问题 -- 数据结构
    列出连通集(DFS及BFS遍历图) -- 数据结构
    42行代码完成深入虎穴
    利用Tarjan算法解决(LCA)二叉搜索树的最近公共祖先问题——数据结构
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10295973.html
Copyright © 2011-2022 走看看