【笔记篇】斜率优化dp(四) ZJOI2007仓库建设

传送门戳这里>>>

n1e6 , 显然还是 O(n) 的做法.
这个题有个条件是只能运往编号更大的工厂的仓库, 这也是写出朴素dp的方程的条件.
我们令 f[i] 表示前 i 个工厂的最小花费, 那么易得

f[i]=min{f[j]+t(j,i)}

其中这个 t(j,i) 表示将(j,i)这个区间的东西运到 i 的总费用. 很显然, 这个式子要O(1)求出来才行, 不然复杂度就要炸…
那么怎么 O(1) 求呢?
考虑类似于前缀和的性质.
这里写图片描述
我们令 si 为将 (1,i] 这个区间中所有工厂的产品运到 i 的总花费, ci表示前 i 个工厂的产品总量, di表示第 i 个工厂的坐标, 我们发现, 如果对i,j做一波前缀和相减, 那么前 j 个点的货物都被多运了didj的距离… 所以就可以推出

t(j,i)=sisjcj(didj)

这样就可以扔进状态转移方程进行斜率优化了… 化完之后的式子是:
f[j]s[j]+c[j]d[j] = d[i] c[j] + f[i]s[i]w[i]
然后求的是最小值, 斜率还递增(这好像是最常见的一种了吧?), 那就跟之前一样咯= =
然而还是把演草纸上 d[i]c[j] 的数组名抄反了WA了一次 但为什么可以过样例啊QAQ
然后就是没有压行的代码: (简单的斜率优化似乎总可以写成标准的20行?

#include <cstdio>
const int N=1e6+6;
typedef long long LL;
LL f[N],s[N],c[N];
int q[N],w[N],d[N],n,h,t;
inline int gn(int a=0,char c=0){
    for(;c<'0'||c>'9';c=getchar());
    for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}
double slope(int x,int y){
    return 1.0*(f[x]-s[x]+c[x]*d[x]-f[y]+s[y]-c[y]*d[y])/(c[x]-c[y]);
}
int main(){
    n=gn(); for(int i=1;i<=n;++i){
        d[i]=gn();c[i]=c[i-1]+gn();w[i]=gn();
        s[i]=s[i-1]+c[i-1]*(d[i]-d[i-1]);
    }
    for(int i=1,j;i<=n;++i){
        while(h<t&&slope(q[h],q[h+1])<=d[i]) ++h; j=q[h];
        f[i]=f[j]+s[i]-s[j]-c[j]*(d[i]-d[j])+w[i];
        while(h<t&&slope(q[t],q[t-1])>=slope(q[t],i)) --t;
        q[++t]=i;
    }
    printf("%lld\n",f[n]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值