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;
    }
  • 相关阅读:
    LeetCode 382. Linked List Random Node
    LeetCode 398. Random Pick Index
    LeetCode 1002. Find Common Characters
    LeetCode 498. Diagonal Traverse
    LeetCode 825. Friends Of Appropriate Ages
    LeetCode 824. Goat Latin
    LeetCode 896. Monotonic Array
    LeetCode 987. Vertical Order Traversal of a Binary Tree
    LeetCode 689. Maximum Sum of 3 Non-Overlapping Subarrays
    LeetCode 636. Exclusive Time of Functions
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10295973.html
Copyright © 2011-2022 走看看