模拟退火

模拟退火

小结:

主要是能够把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
原文地址:https://www.cnblogs.com/Bhllx/p/10360149.html