P2210 Haywire 模拟退火 状压DP
P2210 Haywire 模拟退火 状压DP
在一条轴上给定\(N\)个点,每个点有三个朋友,要求一种排列使得每个点到朋友距离之和之和最小。
\[N\leq 12 ,N \% 2 == 0
\]
分析
状压DP本人学艺不精,没有想到(但好像数据量很像?)
此题可以作为模拟退火的模板题
模拟退火最重要的三个参数:起始温度,终止温度,温度变化速率
模拟退火的关键还在于几率接受
设当前温度为\(T\),当前解为\(k\) ,最优解为\(K_{max}\) ,我们有一定几率接受差解
\[exp((k-max)/T) < (double)rand()/RAND\_MAX
\]
此题可以通过交换两个点的位置来得到新的解
代码
int f[15][3];
int pos[15];
int best = INF;
int n;
int get_cost() {
int tmp = 0;
for (int i = 1; i <= n; i++)
for (int j = 0; j < 3; j++)
tmp += abs(pos[i] - pos[f[i][j]]);
return tmp;
}
const double beginT = 10000, endT = 1e-12, delT = 0.99;
void SA(int times) {
int x, y, tmp;
while (times--) { //模拟退火次数
for (double T = beginT; T > endT; T *= delT) {
do {
x = rand() % n + 1;
y = rand() % n + 1;
} while (x == y);
swap(pos[x], pos[y]);
tmp = get_cost(); //求出当前解
if (tmp <= best)
best = tmp;
else if (exp(best - tmp) / T > (double)rand() / RAND_MAX)
swap(pos[x], pos[y]); //一定几率不接受
}
}
}
int main() {
srand(time(0)); //随机种子
n = readint();
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 3; j++)
f[i][j] = readint();
pos[i] = i;
}
SA(275);
Put(best / 2);
}