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));
}