[斜率优化]BZOJ 1096——仓库建设

本文介绍了一道关于在特定地形条件下建设仓库的问题,并详细阐述了如何利用动态规划及斜率优化技巧来求解最小成本的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1096: [ZJOI2007]仓库建设

题目描述

  L公司有N个工厂,由高到底分布在一座山上。如图所示,工厂1在山顶,工厂N在山脚。由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。突然有一天,L公司的总裁L先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是L先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库的费用是Ci。对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于L公司产品的对外销售处设置在山脚的工厂N,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,假设一件产品运送1个单位距离的费用是1。假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到以下数据:1:工厂i距离工厂1的距离Xi(其中X1=0);2:工厂i目前已有成品数量Pi;3:在工厂i建立仓库的费用Ci;请你帮助L公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。

解题思路

根据题目描述不难想到是DP,很快就会推出方程

f[i]=min(f[j]+c[i]+k=j+1i(x[i]x[k])p[k])

如果提前构造数组 d[] 成品数量的前缀和, w[] 位置和成品数量的前缀和,那么转移方程会变的明显
f[i]=min(f[j]+c[i]+x[i](d[i]d[j])(w[i]w[j]))

把无关项移出来
f[i]=min(f[j]x[i]d[j]+w[j])+x[i]d[i]w[i]+c[i]

k=x[i],x=d[j],y=f[j]+w[j]
通过 b=kx+y,y=kx+b

接下来套用斜率优化就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1000005;
int n,now,til;
LL x[maxn],s_p[maxn],s_xp[maxn],f[maxn],c[maxn];
struct jz{
    LL x,y;
    jz(LL a=0,LL b=0):x(a),y(b){}
    jz operator-(const jz &b){return jz(x-b.x,y-b.y);}
}a[maxn];
inline int _read(){
    int num=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
LL getb(jz x,LL k){return -k*x.x+x.y;}
LL check(jz x,jz y){return x.x*y.y-x.y*y.x;}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    n=_read();
    for (int i=1;i<=n;i++){
        x[i]=_read();int p=_read();c[i]=_read();
        s_p[i]=s_p[i-1]+p;s_xp[i]=s_xp[i-1]+x[i]*p;
    }
    memset(f,63,sizeof(f));
    f[0]=0;now=til=1;a[1]=jz(0,0);
    for (int i=1;i<=n;i++){
        LL k=x[i];
        while (now<til&&getb(a[now+1],k)<getb(a[now],k)) now++;
        f[i]=getb(a[now],k)+x[i]*s_p[i]-s_xp[i]+c[i];
        jz x(s_p[i],s_xp[i]+f[i]);
        while (til>1&&check(a[til]-a[til-1],x-a[til-1])<=0) til--;
        now=min(now,til);a[++til]=x;
    }
    printf("%lld",f[n]);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值