zoukankan      html  css  js  c++  java
  • 模拟退火

    模拟退火

    小结:

    主要是能够把rand的方式弄清:

    ①点的随机化:直接rand。

    ②序列的随机化:rand两个位置交换或rand整个序列。

    ③树的随机化:尽量把其转成序列形式,同序列rand即可。

    接下来只要知道怎样算答案,剩下的基本上就是上板子,注意调参的问题就好了。

    一般以exp(-△ans/T)*RAND_MAX>rand()为条件接受较劣解,注意需保证exp内的值恒为负数。

    上几道题。

    一、平衡点/吊打XXX

    点的随机,直接rand就好了。

    然后答案的计算,ans=∑(dis*w[i]);

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline
    #define DB double 
    #define LL long long
    using namespace std;
    
    IL int gi() {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=1e3+10;
    const DB dT=0.99;
    const DB eps=1e-14;
    
    int n;
    DB x,y,ans=1e18+0.1;
    
    struct Object{DB x,y,w;}ob[N];
    
    IL DB Rand() {return (DB)(rand()*rand()-RAND_MAX);}
    
    IL DB get_sum(DB x,DB y) {
        RG int i;
        RG DB sum=0;
        for (i=1;i<=n;++i)
            sum+=sqrt((ob[i].x-x)*(ob[i].x-x)+(ob[i].y-y)*(ob[i].y-y))*ob[i].w;
        return sum;
    }
    
    IL void SA() {
        RG DB ax=x,ay=y,nowx,nowy,Nans;
        RG DB T=70000,Dans;
        while (T>=eps) {
            nowx=ax+Rand()*T,nowy=ay+Rand()*T;
            Nans=get_sum(nowx,nowy);
            Dans=Nans-ans;
            if (Dans<0) ans=Nans,ax=x=nowx,ay=y=nowy;
            else if (exp(-Dans/T)*RAND_MAX>rand()) ax=nowx,ay=nowy;
            T*=dT;
        }
    }
    
    int main ()
    {
        RG int i;
        for (i=1,n=gi();i<=n;++i)
            scanf("%lf%lf%lf",&ob[i].x,&ob[i].y,&ob[i].w);
        srand(time(0)),SA();
        printf("%lf %lf
    ",x,y);
        return 0;
    }
    BY BHLLX

    二、均分数据

    序列的随机,rand两个位置交换。

    答案统计用动态规划:

    f[i][j]=f[i][j]=min{f[k][j-1]+(pre[i]-pre[k]-ave)*(pre[i]-pre[k]-ave)};

    刚做这个题时,too naive。。。交了20多发。

    发现只模拟退火一次总会错一点。后面发现,初始温度设低一点,多跑几次正确率高些。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline
    #define DB double 
    #define LL long long
    using namespace std;
    
    IL int gi() {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=21;
    const DB dT=0.99;
    const DB eps=1e-10;
    
    DB ave,Ans=1e18,pre[N],f[N][N];
    int n,m,a[N];
    
    IL DB Work() {
        RG int i,j,k;
        memset(f,127,sizeof(f));
        memset(pre,0,sizeof(pre));
        f[0][0]=0;
        for (i=1;i<=n;++i) pre[i]=pre[i-1]+a[i];
        for (i=1;i<=n;++i)
            for (j=1;j<=i&&j<=m;++j)
                for (k=0;k<i;++k)
                    f[i][j]=min(f[i][j],f[k][j-1]+(pre[i]-pre[k]-ave)*(pre[i]-pre[k]-ave));
        return f[n][m];
    }
    
    void simulate_A() {
        RG DB T=6666,ans;
        RG int nx,ny;
        while (T>=eps) {
            if (Ans==0) break;
            nx=rand()%n+1,ny=rand()%n+1;
            while (nx==ny) nx=rand()%n+1,ny=rand()%n+1;
            swap(a[nx],a[ny]);
            ans=Work();
            if (ans<Ans) Ans=ans;
            else if (exp((Ans-ans)/T)*RAND_MAX<=rand()) swap(a[nx],a[ny]);
            T*=dT;
        }
    }
    
    int main ()
    {
        srand(19260817);
        RG int i;
        n=gi(),m=gi();
        for (i=1;i<=n;++i) a[i]=gi(),ave+=a[i];
        ave=ave/(DB)m,Ans=Work();
        for (i=1;i<=66;++i) simulate_A();
        printf("%.2lf
    ",sqrt(Ans/(DB)m));
        return 0;
    }
    BY BHLLX

    三、分金币

    序列的随机,rand两个位置交换。

    答案统计,直接从前往后扫一遍,前后两半的差作为结果。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline
    #define DB double 
    #define LL long long
    using namespace std;
    
    IL int gi() {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=51;
    const int INF=0x3f3f3f3f;
    const DB dT=0.99;
    const DB eps=1e-10;
    
    int n,ans,a[N];
    
    IL int Work() {
        RG int i,sum1=0,sum2=0;
        for (i=1;i<=n;++i)
            if (i<=(n+1>>1)) sum1+=a[i];
            else sum2+=a[i];
        return abs(sum1-sum2);
    }
    
    void simulate_A() {
        RG DB T=5000;
        RG int x,y,Minx;
        while (T>=eps) {
            x=rand()%(n+1>>1)+1,y=rand()%(n>>1)+(n+1>>1)+1;
            swap(a[x],a[y]);
            Minx=Work();
            if (Minx<ans) ans=Minx;
            else if (exp((DB)(ans-Minx)*1.0/T)*RAND_MAX<=rand()) swap(a[x],a[y]);
            T*=dT;
        }
    }
    
    int main ()
    {
    
        srand(time(0));
        RG int i,T=gi();
        while (T--) {
            n=gi(),ans=INF;
            for (i=1;i<=n;++i) a[i]=gi();
            if (n==1) {printf("%d
    ",a[1]);continue;}
            for (i=1;i<=20;++i) simulate_A();
            printf("%d
    ",ans);
        }
        return 0;
    }
    BY BHLLX

    四、Haywire

    基本一样。。。好像没什么可讲了。。。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline
    #define DB double 
    #define LL long long
    using namespace std;
    
    IL int gi() {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=13;
    const DB dT=0.99;
    const DB eps=1e-10;
    
    int n,ans,a[N][N],P[N];
    
    IL int Rand(int x) {return rand()%n+1;}
    
    IL int calc() {
        RG int i,j,cnt=0;
        for (i=1;i<=n;++i)
            for (j=i+1;j<=n;++j)
                if (a[i][j]) cnt+=abs(P[i]-P[j]);
        return cnt;
    }
    
    IL void simulate_A() {
        RG DB T=6666;
        RG int x,y,now;
        while (T>=eps) {
            x=Rand(n),y=Rand(n);
            while (x==y) x=Rand(n),y=Rand(n);
            swap(P[x],P[y]);
            now=calc();
            if (now<ans) ans=now;
            else if (exp((ans-now)/T)*RAND_MAX<=rand()) swap(P[x],P[y]);
            T*=dT;
        }
    }
    
    int main ()
    {
        srand(time(0));
        RG int i,j,x;
        for (i=1,n=gi();i<=n;++i)
            for (j=1;j<=3;++j)
                x=gi(),a[i][x]=a[x][i]=1;
        for (i=1;i<=n;++i) P[i]=i;
        ans=calc();
        for (i=1;i<=20;++i) simulate_A();
        printf("%d
    ",ans);
        return 0;
    }
    BY BHLLX

    五、宝藏

    状压做法

    这里用模拟退火来做。

    这是树上问题。怎么来随机呢?

    我们还是尽量把它转成序列来做:

    不妨把每一个节点先扔在一个序列中,然后考虑这棵树的生成。

    我们可以按照序列的顺序,依次把节点加入树中,

    贪心的来选择当前节点与之前哪个节点连边。

    如果发现当前序列下,无法生成一棵树,那么就退出。

    这样的话,我们就可以按照序列的随机那样,rand两个节点交换就好了。

    统计答案的话,就是在生成树的过程中,一起计算了。

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline
    #define DB double 
    #define LL long long
    using namespace std;
    
    IL int gi() {
        RG int x=0,w=0; char ch=0;
        while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
        return w?-x:x;
    }
    
    const int N=15;
    const int INF=0x3f3f3f3f;
    const DB dT=0.99;
    const DB eps=1e-10;
    
    int n,m,ans,P[N],ver[N][N],dep[N];
    
    IL int solve() {
        RG int i,j,cost,Ans=0;
        dep[P[1]]=1;
        for (i=2;i<=n;++i) {
            for (j=1,cost=INF;j<i;++j)
                if (ver[P[i]][P[j]]&&dep[P[j]]*ver[P[i]][P[j]]<cost)
                    cost=dep[P[j]]*ver[P[i]][P[j]],dep[P[i]]=dep[P[j]]+1;
            if (cost==INF) return INF;
            Ans+=cost;
        }
        return Ans;
    }
    
    IL void simulate_A() {
        RG DB T=8888;
        RG int x,y,now;
        while (T>=eps) {
            x=rand()%n+1,y=rand()%n+1;
            while (x==y) x=rand()%n+1,y=rand()%n+1;
            swap(P[x],P[y]),now=solve();
            if (now<ans) ans=now;
            else if (exp((ans-now)/T)*RAND_MAX<=rand()) swap(P[x],P[y]);
            T*=dT;
        }
    }
    
    int main ()
    {
        srand(time(0));
        RG int i,x,y,z;
        n=gi(),m=gi();
        if (m==0) {puts("0");return 0;}
        for (i=1;i<=m;++i) {
            x=gi(),y=gi(),z=gi();
            if (x==y) continue;
            if (z<ver[x][y]||!ver[x][y]) ver[x][y]=ver[y][x]=z;
        }
        for (i=1;i<=n;++i) P[i]=i;
        ans=solve();
        for (i=1;i<=66;++i) simulate_A();
        printf("%d
    ",ans);
        return 0;
    }
    BY BHLLX
  • 相关阅读:
    【Android】带底部指示的自定义ViewPager控件
    【Android】百度地图自定义弹出窗口
    【Linux配置】vim配置文件内容
    【Linux Tips】登陆,提示符,别名
    读《effective C++》2
    读《effective C++》1
    【PAT_Basic日记】1005. 继续(3n+1)猜想
    【PAT_Basic日记】1004 成绩排名
    【PAT_Basic日记】1002. 写出这个数
    【PAT_Basic日记】1001. 害死人不偿命的(3n+1)猜想
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10360149.html
Copyright © 2011-2022 走看看