Loading

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);
}
posted @ 2020-09-06 21:30  MQFLLY  阅读(173)  评论(0编辑  收藏  举报