zoukankan      html  css  js  c++  java
  • 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));
    }
    
  • 相关阅读:
    多线程锁--怎么理解Condition
    ThreadPoolExecutor
    ThreadFactory
    java内部类的初始化
    Android Private Libraries 和 Dependencies的区别
    Android严苛模式StrictMode使用详解
    [法律法规]《网络安全等级保护条例(征求意见稿)》
    [法律法规]中华人民共和国网络安全法
    Sqlserver tablediff的简单使用
    Sqlserver 命令行方式修改 用户密码的方法
  • 原文地址:https://www.cnblogs.com/hznumqf/p/13627268.html
Copyright © 2011-2022 走看看