一些乱搞的退火题

退火(全称叫模拟退火)是一种随机乱搞的玄学算法,大概是珂学家们发现随机贪心往往效果不好,所以出现了在原策略上进行改动的方法。后来发现单纯的小改动容易陷入局部最优解,就又有了这个玄学的退火。

不过我并不想讲这个具体的过程,只是简单说一些细节:
1、记住 e − a n s / t e^{-ans / t} eans/t是接受解的概率( a n s ans ans指当前解与最优解差了多少),很良心的,我给出这个函数的一些结果作为参考:
e − 0.001 = 0.9990005 e^{-0.001}=0.9990005 e0.001=0.9990005
e − 0.01 = 0.99005 e^{-0.01}=0.99005 e0.01=0.99005
e − 0.1 = 0.905 e^{-0.1}=0.905 e0.1=0.905
e − 1 = 0.36788 e^{-1}=0.36788 e1=0.36788
e − 10 = 0.0000454 e^{-10}=0.0000454 e10=0.0000454
e − 100 = 0.0000000000000000000000000000000000000000000372 e^{-100}=0.0000000000000000000000000000000000000000000372 e100=0.0000000000000000000000000000000000000000000372
所以你的退火也是有脑子的!

2、温度既可以决定接受更差解的概率,也可以决定在当前策略上调整的步幅,当然啦,温度越高步幅就越大了。

3、调整决策的具体方法和数值比较随意,为了方便,如果决策由一个序列决定,我们可以随机交换两个值来得到新决策(这个时候步幅跟温度无关)。如果是平面或空间中实数域上的一个点,我们可以向随机一个方向移动与温度呈正相关的距离。如果是整数域,我们可以把温度四舍五入当做步幅。

4、 s r a n d ( 19260817 ) srand(19260817) srand(19260817)之后再 s r a n d ( r a n d ( ) ) srand(rand()) srand(rand())两次有奇效,初温末温和 Δ \Delta Δ根据题目来定,据说在得到解的优化率比较平均时能得到较好的结果。

5、如果是传统题,可以加入卡时操作。尽管你有可能会得到洛谷rank-1,但是你稳呀

提供个板子(伪代码):

inline void SA()
{
    double t = t0;
    while (t > t1) {
    	get_a_new_solution(t);
        double ans = calc(now) - calc(pre);
        if (ans < 0) pre = now, best_answer = calc(now);
        else if (exp(-ans / t) * RAND_MAX > rand()) pre = now;
        t *= delta;
    }
}

看起来十分 e a z y eazy eazy

然后是一些水题:
P1337 [JSOI2004]平衡点 / 吊打XXX
XXX是有很多妹子的神仙GTY
把上面板子改改就好了
P2503 [HAOI2006]均分数据
每次随出一个新序列,用朴素的三方 d p dp dp去判断解的优越性
P4360 [CEOI2004]锯木厂选址
每次随出两个点来,代回去 O ( 1 ) O(1) O(1)判断

顺便补一下第三题的斜率优化做法:

#include <iostream>
#include <cstring>
#include <cstdio>

const int maxn = 2e4 + 7;

using namespace std;

int n, ans = 2e9;
int s[maxn], d[maxn];
int v[maxn];
int f[maxn];
int q[maxn], l, r;

inline int read()
{
    int X = 0; char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') X = X * 10 + ch - '0', ch = getchar();
    return X; 
}

inline double k(int i, int j)
{
    return 1.0 * (d[j] * s[j] - d[i] * s[i]) / (s[j] - s[i]);
}

int main(void)
{
    cin >> n;
    for (int i = 1; i <= n; i++) s[i] = s[i - 1] + read(), d[i] = read();
    for (int i = 1; i <= n + 1; i++) v[i] = v[i - 1] + s[i - 1] * d[i - 1];
    for (int i = n; i; i--) d[i] += d[i + 1];
    q[++r] = 1;
    for (int i = 2; i <= n; i++) {
        while (l < r && (d[q[l]] - d[i]) * s[q[l]] < (d[q[l + 1]] - d[i]) * s[q[l + 1]]) l++;
        f[i] = v[n + 1] - d[i] * s[i] - (d[q[l]] - d[i]) * s[q[l]];
        while (r > l + 1 && k(i, q[r]) > k(i, q[r - 1])) r--;
        q[++r] = i;
    }
    for (int i = 2; i <= n; i++) ans = min(ans, f[i]);
    cout << ans << endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值