模拟退火
模拟退火是一种通过随机化算法得出一类题目的最优方案的算法。
模拟退火时,如果当前状态比目前最优解更优则更新最优解,否则概率接受该状态(而不是直接将该状态给不人性化地 pass 掉),这个概率会不断减小,直到最后找到最优解。
为什么要这样做呢?对于这类最优化问题,我们如果把所有可能的状态集合的解给排成一列,那么会得到一个答案关于状态的一个函数 \(f(S)\):

很明显其不一定是一个单峰函数,因此爬山算法不可行,而如果我们不概率接受看起来更劣的状态,那么就会拘泥于一个山峰而不能看到更远的更高的山峰。
模拟退火算法需要借助几个参数实现:初始温度 \(T_0\),终止温度 \(T_e\),降温系数 \(d\)。其中 \(T_0\) 为较大正数(一般在 \(10^3\) 及以上级别),\(T_e\) 为较小正数(一般在 \(10^{-15}\) 左右的级别),\(d\) 为一个接近 \(1\) 的实数(一般取 \(0.9\sim 1\) 中的数)。
退火前,让当前温度 \(T=T_0\)。然后尝试随机扰动转换到另一个状态,如果该状态的答案比当前最优解更优则更新最优解,否则以 \(P=\exp\left ( -\dfrac{\Delta ans}{T} \right )\) 的概率更改状态,其中 \(\Delta ans\) 为当前最优解答案与该状态答案的差值。更改完状态后,让温度 \(T\) 乘以 \(d\)(降温)。重复操作直到 \(T\le T_e\)。
#include<bits/stdc++.h>
using namespace std;
const int N = 35, INF = INT_MAX;
int T, n, tpans, ans;
int a[N];
double Rand()
{
return (double)rand() / RAND_MAX;
}
void work()
{
int ans1 = 0, ans2 = 0;
for(int i = 1; i <= (n + 1) / 2; i ++)
{
ans1 += a[i];
}
for(int i = (n + 1) / 2 + 1; i <= n; i ++)
{
ans2 += a[i];
}
tpans = abs(ans1 - ans2);
}
void SA()//模拟退火
{
double T, T0 = 2500, d = 0.985, Te = 1e-15;
T = T0;
while(T > Te)
{
int x = rand() % n + 1;
int y = rand() % n + 1;
swap(a[x], a[y]);
work();
if(tpans < ans)
{
ans = tpans;
}
else if(exp((double)(ans - tpans) / T) < Rand())
{
swap(a[x], a[y]);
}
T *= d;
}
}
void solve()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
}
ans = INF;
for(int i = 1; i <= 100; i ++)
{
SA();
}
cout << ans << "\n";
}
int main()
{
srand(time(0));
cin >> T;
while(T --)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号