模拟退火
流程与爬山算法类似,在基础上加入退火操作。
如爬山里的图绿色部分所示,兔子有一定概率朝更矮的方向跳,这使它在不是最高的山上也有可能跳到最高的山上。
过程
如果新状态更优就更新状态,否则以一定概率更新。
定义当前温度为 \(T\),新状态 \(S'\) 与已知状态 \(S\)(新状态由已知状态通过随机的方式得到)之间的能量(值)差为 \(\Delta E\)( \(\Delta E\geqslant 0\)),则不优情况下更新解的概率为 \(e^\frac{-\Delta E}{T}\)。
下面是一个比较形象的演示动图:

一些细节
- 温度和计算状态的参数对结果影响很大,可以对拍来调参数。
- 为保证准确性,可以多执行几次模拟退火。
- 即使如此,也可能一次提交无法通过,可以换随机种子多次尝试。
例题一
P2503 [HAOI2006] 均分数据
已知 \(n\) 个正整数 \(a_1,a_2 ... a_n\) 。要将它们分成 \(m\) 组,使得各组数据的数值和最平均,即各组数字之和的均方差最小。
对于一种给定的顺序,一个很容易想到的操作方案是将 \(a\) 插进当前和最小的一组。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
其实卡时+每次随机一个排列就可以过了。
每次随机选择两个位置并交换,如果所得的结果更优则更新数组,否则有一定概率更新。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
例题二
P5544 [JSOI2016] 炸弹攻击1
平面上有 \(n\) 个建筑(可视为圆)与 \(m\) 个敌人。你可以在任意位置放置半径不超过 \(R\) 的圆,其范围内不能有任何建筑,求其能包含的最多的敌人数。
该题中答案为整数且范围很小,如果只以答案为状态,函数不平滑,而且有很多位置受建筑影响答案为 \(0\),这使得函数被分成很多段。 考虑在状态中加入新成分。经过思考可以想到将所有答案为 \(0\) 的位置的状态设为「当前圆为碰到第一个敌人还需增大的半径」,再通过尝试可以找到一个合适的系数。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
例题三
P3936 Coloring
按要求在 \(n \times m\) 的格子上染色,求使得相邻格子颜色不同的数量最少的染色方案。
每次随机交换两个格子的颜色,能使答案更优就保留操作,否则概率保留。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |