题目大意:
一座山,山脚下有个锯木厂,山上有 (n) 棵树,把一棵树砍掉的费用是它的重量乘离锯木厂的距离,如果再在山上设置两个锯木厂,求把所有树砍掉的最小费用。
正文:
考虑直接用动态规划。设 (f_i) 表示第二个锯木厂设置在 (i) 处的最小费用,(F_{i,j}) 表示从 ((i+1)) 到 (j) 这一段的费用(即在 (i) 和 (j) 处建锯木厂时,从 ((i+1)) 到 (j) 的费用),那么动态转移方程就是:
[f_i=operatorname{min}_{j=1}^{i-1}{F_{0,j}+F_{j,i}+F_{i,n+1}}
]
接下来考虑 (F_{i,j})。
[F_{i,j}=sum_{k=i+1}^{j-1}left(sum_{l=k+1}^{j-1}d_l
ight)w_k
]
其中 (sum_{l=k+1}^{j}d_l),完全可以用前缀和预处理出,这里用 (D_i) 表示(这里 (D_i=sum_{j=1}^{i-1}d_j)):
[egin{aligned}F_{i,j} & = sum_{k=i+1}^{j-1}left(D_j-D_k
ight)w_k \ & = sum_{k=i+1}^{j-1}D_jcdot w_k-D_kcdot w_k \ & = left(sum_{k=i+1}^{j-1}w_k
ight)D_j-left(sum_{k=i+1}^{j-1}w_kcdot D_k
ight)end{aligned}
]
其中 (sum_{k=i+1}^{j-1}w_k) 和 (sum_{k=i+1}^{j-1}w_kcdot D_k) 可以用前缀和预处理,这里用 (W_i,S_i) 表示(这里 (S_i=sum_{j=1}^{i}w_jcdot D_j,W_i=sum_{j=1}^{i}w_j)):
[egin{aligned}F_{i,j} & =left(W_{j-1}-W_i
ight)D_j-left(S_{j-1}-S_i
ight)\ & = W_{j-1}cdot D_j-W_icdot D_j-S_{j-1}+S_iend{aligned}
]
则:
[f_i=min_{j=1}^{i-1}{left(W_{j-1}cdot D_j-W_0cdot D_j-S_{j-1}+S_0
ight)+left(W_{i-1}cdot D_i-W_jcdot D_i-S_{i-1}+S_j
ight)+left(W_{n}cdot D_{n+1}-W_icdot D_{n+1}-S_{n}+S_i
ight)}
]
假设有 (j,k(j,k<i)) 且 (j) 的决策比 (k) 更优的情况:
[left(W_{j-1}cdot D_j-W_0cdot D_j-S_{j-1}+S_0
ight)+left(W_{i-1}cdot D_i-W_jcdot D_i-S_{i-1}+S_j
ight)+left(W_{n}cdot D_{n+1}-W_icdot D_{n+1}-S_{n}+S_i
ight)leqleft(W_{k-1}cdot D_k-W_0cdot D_k-S_{k-1}+S_0
ight)+left(W_{i-1}cdot D_i-W_kcdot D_i-S_{i-1}+S_k
ight)+left(W_{n}cdot D_{n+1}-W_icdot D_{n+1}-S_{n}+S_i
ight)
]
最后可求得斜率方程:
[frac{(W_{j-1} imes D_{j}-S_{j-1}+S_{j})-(W_{i-1} imes D_i-S_{i-1}+S_i)}{W_j-W_i} leq D_i
]
代码:
double slope(int x, int y)
{
return (double)((w[y - 1] * sumd[y] - a[y - 1] + a[y]) - (w[x - 1] * sumd[x] - a[x - 1] + a[x]) - 0.0) / (w[y] - w[x] + 0.0);
}
ll ans = 1ll << 60;
int main()
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
scanf ("%lld", &n);
for (int i = 1; i <= n; ++i)
scanf ("%lld%lld", &w[i], &d[i]);
for (int i = 1; i <= n + 1; ++i)
{
sumd[i] = sumd[i - 1] + d[i - 1];
a[i] = a[i - 1] + w[i] * sumd[i];
w[i] += w[i - 1];
}
for (int i = 1; i <= n; i++)
{
while(head < tail && slope(que[head], que[head + 1]) <= (double)sumd[i])
head++;
f[i] = (w[que[head] - 1] * sumd[que[head]] - a[que[head] - 1]) + (w[i - 1] * sumd[i] - w[que[head]] * sumd[i] - a[i - 1] + a[que[head]]) + (w[n] * sumd[n + 1] - w[i] * sumd[n + 1] - a[n] + a[i]);
ans = min (ans, f[i]);
while(head < tail && slope(que[tail], i) <= slope(que[tail - 1], que[tail]))
tail--;
que[++tail] = i;
}
printf ("%lld", ans);
return 0;
}