随机化与SA学习笔记

SA

今天翻出了很久之前给自己安排做的题

P4035 [JSOI2008]球形空间产生器

结果我把高斯消元忘了,想起之前拿随机化贪心骗分的快乐,于是学习了另一种解法A掉这道题。

看标签都知道,模拟退火

我打的第一个模拟退火没用随机化,珂以算作爬山。

首先,我们为了尽可能的得到最快最优的答案。先把初始状态设为每一维度的平均值。

以二维圆形为例子(样例):

woxihuan heyifei

我们找到当前状态与每个点的距离平均值。若小于这个平均值则往远离该点的方向移动,反之靠近该点。

具体移动多少,枚举每一维,然后把该维答案与该点的差乘以平均值与距离之差与平均值的比值。

这部分的代码是:

for(int i=1;i<=n+1;i++)
   for(int j=1;j<=n;j++)
    cha.x[j]+=((dis[i]-ave))*(a[i].x[j]-ans.x[j])/ave;

cha就是每一维的改变值

最后根据模拟退火的温度变化,把温度设为一个系数,乘以差值在加上当前状态,累加后的当前状态经过降温后又将重复操作,直到温度达到最低温度。

这是累加代码

for(int i=1;i<=n;i++)ans.x[i]+=cha.x[i]*t;

这是模拟退火的关键(温度变化):

for(double t=10000;t>0.0001;t*=0.99996)sa(t);

最后,我们用玄学做法AC了这道省选紫题。

下面我们再看一道SA的模板题 P1337 [JSOI2004] 平衡点 / 吊打XXX

前置知识:力的合成与分解。

若不知道力的合成与分解,计算合力可用一下代码。

double energy(double x,double y){
    double ans=0;
    for(int i=1;i<=n;i++)ans+=dist(x,y,a[i].x,a[i].y)*a[i].w;
    return ans;
}

意思就是把这个点拨到x,y位置,若增加一个外力卡住正好可以平衡,这个外力就珂以用这个函数计算。

我们的目的就是找到一个值使得这个合力尽可能小。有两种办法。

第一个是两次三分法找到二元单峰函数的最小值,这里不再展开介绍。

第二种就是模拟退火。

其实还有计算几何的做法我不会(doge)。

我们在目前的x,y上各增加一个随机值,然后对比energy的大小比较。

如果小于energy或者差值可以被接受,则把答案设为随机增加后的新答案。

同时降温,使得x,y的变化趋势减弱。

这里给出核心代码

double sa(){
    for(double t=1145;t>eps;t*=0.999){
        double tx=ansx+(rand()*2-fi)*t,ty=ansy+(rand()*2-fi)*t;
        double del=energy(tx,ty)-energy(ansx,ansy);
        if(del<0)ansx=tx,ansy=ty;
        else if(exp(-del/t)*fi>rand())ansx=tx,ansy=ty;
    }
}

随机化

P2210 Haywire,这题珂以算是随机化的模板题

每个牛有三个朋友,可以随机排列牛。( N≤12N12 )

你是不是想全排列爆搜?

然鹅 12!≈4\times10^812!4×108 时间是不优秀的。

于是我们可以随机化贪心。

随机构造一个奶牛的顺序,然后模拟铁路的长度即可,因为是来回的,所以答案要除以2。

这时候一次贪心的代码:

void solve(){
    random_shuffle(p+1,p+n+1);
    for(int i=1;i<=n;i++)id[p[i]]=i;
    int res=0;
    for(int i=1;i<=n;i++){
        int now=id[f[i].zj],a=id[f[i].a],b=id[f[i].b],c=id[f[i].c];
        res+=abs(now-a)+abs(now-b)+abs(now-c);
    } 
    ans=min(ans,res);
}

我粗略试了一下,随机化 10^6106 次即可AC,远远优于 4\times10^84×108

在结尾,我送上一道好题

完结撒花

 
posted @ 2022-08-25 11:47  灵长同志  阅读(71)  评论(0)    收藏  举报