CEOI2017 锯木厂选址 斜率优化状压DP 模拟退火
CEOI2017 锯木厂选址 斜率优化状压DP 模拟退火
题意
一座山上有\(n\) 棵树,山脚有一锯木厂,现要在这\(n\)个位置中再造两个锯木厂使得总耗费最小。耗费 = 这棵树的重量乘与锯木厂的距离。且只能向下运输。
\[2\leq n \leq 2 \cdot 10 ^ 4, 1 \leq w_i\leq10^4,0\leq d_i\leq 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_a\cdot \sum_{i=1}^a w_i - \sum_{i=1}^a w_i\cdot D_i + D_b\cdot \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_i\cdot D_i
\\
=D_a\sum_{i = 1}^aw_i + D_b\sum_{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));
}