CEOI2017 锯木厂选址 斜率优化状压DP 模拟退火

CEOI2017 锯木厂选址 斜率优化状压DP 模拟退火

题意

一座山上有(n) 棵树,山脚有一锯木厂,现要在这(n)个位置中再造两个锯木厂使得总耗费最小。耗费 = 这棵树的重量乘与锯木厂的距离。且只能向下运输。

[2leq n leq 2 cdot 10 ^ 4, 1 leq w_ileq10^4,0leq d_ileq 10^4 \ d_i 表示i与i+1的距离,d_n表示第n棵树与山脚锯木厂的距离 ]

分析

首先注意到的性质,我们希望能O(1)计算出总耗费

不妨设(D_i = sum_{j=1}^{i-1} d_j) 则有

[f = sum_{i=1}^a w_i cdot (D_a - D_i) + sum_{i=a+1}^b w_i cdot (D_b-D_i)+sum_{i=b+1}^n w_i cdot (D_{n+1} - D_i) \ =D_acdot sum_{i=1}^a w_i - sum_{i=1}^a w_icdot D_i + D_bcdot sum_{i=a+1}^b w_i- sum_{i=a+1}^b w_i cdot D_i + D_{n+1}cdot sum_{i=b+1}^nw_i - sum_{i=b+1}^nw_icdot D_i \ =D_asum_{i = 1}^aw_i + D_bsum_{i=a+1}^bw_i + D_{n+1}sum_{i=b+1}^n w_i - sum_{i=1}^n w_i cdot D_i ]

(W_i = sum w_i , s = sum w_i D_i)

[f = D_a W_a + D_b(W_b - W_a) + D_{n+1}(W_n - W_b) - s ]

然后就是模拟退火的实现了,我们参照那张经典的图,以一定的概率接受差解,随机出新的偏移量(温度越低偏移越小)。

代码

ll D[200005];
ll W[200005];
ll s;
int n;

ll cal(int pos1,int pos2) {
    if (pos2 < pos1) swap(pos1, pos2);
    return D[pos1 - 1] * W[pos1] + D[pos2 - 1] * (W[pos2] - W[pos1]) + D[n] * (W[n] - W[pos2]) - s;
}

ll best;
double beginT = 100, endT = 5e-5, delT = 0.99;
int pos1 = 1, pos2 = 2;

void SA(int times) {
    while (times--) {
        int tmp1 = rand() % n + 1;
        int tmp2 = rand() % n + 1;
        beginT = 1.0 * n;
        for (double T = beginT; T >= endT; T *= delT) {
            int xx = round((2.0 * rand() / RAND_MAX - 1) * T);
            int yy = round((2.0 * rand() / RAND_MAX - 1) * T);
            int x, y;
            x = ((xx + tmp1) % n + n) % n + 1;
            y = ((yy + tmp2) % n + n) % n + 1;
            ll t = cal(x, y);
            ll tt = cal(tmp1, tmp2);
            if (t < tt) tmp1 = x, tmp2 = y;
            else {
                if (exp(tt - t) / T >= (double)rand() / RAND_MAX)  tmp1 = x, tmp2 = y;
            }
            if (tt < cal(pos1, pos2)) pos1 = tmp1, pos2 = tmp2;
        }
    }
}

int main() {
    srand(time(0));
    n = readint();
    for (int i = 1; i <= n; i++) {
        ll w = readll();
        ll d = readll();
        W[i] = W[i - 1] + w;
        D[i] = D[i - 1] + d;
        s += w * D[i - 1];
    }
    SA(100);
    Put(cal(pos1, pos2));
}
原文地址:https://www.cnblogs.com/hznumqf/p/13627268.html