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;
}