[BZOJ1096][ZJOI2007]仓库建设(斜率优化dp)

仿佛终于理解了斜率优化

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=1096

Solution

显然 dp 。
定义状态:
f[i] f [ i ] 表示在前 i i 个工厂建立仓库,且第 i 个工厂必须建立仓库的最小费用。
转移就是枚举上一个仓库的建立位置 j j (如果没有则 j=0

f[i]=minj=0i1{f[j]+k=j+1i{(XiXk)×Pk}+Ci} f [ i ] = min j = 0 i − 1 { f [ j ] + ∑ k = j + 1 i { ( X i − X k ) × P k } + C i }

考虑把 ik=j+1{(XiXk)×Pk} ∑ k = j + 1 i { ( X i − X k ) × P k } 化开:
S S P 的前缀和, R R P×X 的前缀和)
(即 Si=ij=1Pj S i = ∑ j = 1 i P j Ri=ij=1{Pj×Xj} R i = ∑ j = 1 i { P j × X j }
k=j+1i{(XiXk)×Pk}=k=j+1i{(Xi×PkXk×Pk} ∑ k = j + 1 i { ( X i − X k ) × P k } = ∑ k = j + 1 i { ( X i × P k − X k × P k }

=k=j+1i{Xi×Pk}k=j+1i{Xk×Pk} = ∑ k = j + 1 i { X i × P k } − ∑ k = j + 1 i { X k × P k }

=Xi×k=j+1iPkk=j+1i{Xk×Pk} = X i × ∑ k = j + 1 i P k − ∑ k = j + 1 i { X k × P k }

=Xi×(SiSj)(RiRj) = X i × ( S i − S j ) − ( R i − R j )

于是:
f[i]=minj=0i1{f[j]+Xi×(SiSj)(RiRj)+Ci} f [ i ] = min j = 0 i − 1 { f [ j ] + X i × ( S i − S j ) − ( R i − R j ) + C i }

=minj=0i1{f[j]+Xi×SiXi×SjRi+Rj+Ci} = min j = 0 i − 1 { f [ j ] + X i × S i − X i × S j − R i + R j + C i }

=minj=0i1{f[j]+RjXi×Sj}+Xi×SiRi+Ci = min j = 0 i − 1 { f [ j ] + R j − X i × S j } + X i × S i − R i + C i

这是一个显然的斜率优化。
k<j<i k < j < i ,那么从 j j 转移比 k 优当且仅当:
f[j]+RjXi×Sj<f[k]+RkXi×Sk f [ j ] + R j − X i × S j < f [ k ] + R k − X i × S k

(f[j]+Rj)(f[k]+Rk)<Xi×SjXi×Sk ( f [ j ] + R j ) − ( f [ k ] + R k ) < X i × S j − X i × S k

(f[j]+Rj)(f[k]+Rk)SjSk<Xi ( f [ j ] + R j ) − ( f [ k ] + R k ) S j − S k < X i

左边是一个斜率的式子!
把每个决策看作一个平面上一个点:决策为 i i (Si,f[i]+Ri)
考虑 a<b<c a < b < c ,设 slope[a,b] s l o p e [ a , b ] 为过第 a a 个点与第 b 个点的直线的斜率,
并且 slope[a,b]>slope[b,c] s l o p e [ a , b ] > s l o p e [ b , c ]
(1)如果 slope[a,b]<Xi s l o p e [ a , b ] < X i ,那么 slope[b,c] s l o p e [ b , c ] 一定也小于 Xi X i 。这时候决策 c c 比决策 b 优。
(2)如果 slope[b,c]>Xi s l o p e [ b , c ] > X i ,那么 slope[a,b] s l o p e [ a , b ] 一定也大于 Xi X i 。那么这时候决策 b b 一定不比决策 a 优。
(3)如果 slope[b,c]<Xi<slope[a,b] s l o p e [ b , c ] < X i < s l o p e [ a , b ] ,那么决策 b b 一定不比决策 a c c 优。
得出结论:如果 slope[a,b]>slope[b,c] ,那么决策 b b 一定不会是最优决策。
所以,所有可能成为最优决策的点构成一个下凸壳,斜率单调递增。
并且,如果一个决策 j 在下凸壳上是最优决策(设 k k j 在下凸壳上的前一个点, h h j 在凸壳上的后一个点),那么一定有 slope[k,j]<Xi,slope[j,h]>Xi s l o p e [ k , j ] < X i , s l o p e [ j , h ] > X i 。从这里也能得出,这个动规方程在下凸壳上满足决策单调性。
根据斜率的单调性,可以用一个单调队列维护下凸壳,每次进行决策时,利用决策的单调性,从队首不断出队,找到最优决策 j j 并转移。每次决策结束后,在下凸壳末尾加入点 (Si,f[i]+Ri) ,并维护下凸的性质。
由于每个点最多进出队一次,所以复杂度 O(n) O ( n )

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll; const int N = 1e6 + 5;
int n, X[N], P[N], C[N], H, T, que[N]; ll f[N], S[N], R[N], x[N], y[N];
ll calc(int j, int i) {return y[j] - x[j] * X[i];}
double slope(int j, int k) {
    return 1.0 * (y[j] - y[k]) / (x[j] - x[k]);
}
int main() {
    int i; n = read(); For (i, 1, n) X[i] = read(), P[i] = read(), C[i] = read();
    For (i, 1, n) S[i] = S[i - 1] + P[i], R[i] = R[i - 1] + 1ll * P[i] * X[i];
    f[0] = x[0] = y[0] = 0; que[H = T = 1] = 0; For (i, 1, n) {
        while (H < T && calc(que[H], i) >= calc(que[H + 1], i)) H++;
        f[i] = calc(que[H], i) + 1ll * S[i] * X[i] - R[i] + C[i];
        x[i] = S[i]; y[i] = f[i] + R[i];
        while (H < T && slope(que[T - 1], que[T]) > slope(que[T], i)) T--;
        que[++T] = i;
    }
    cout << f[n] << endl; return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值