仓库建设
题目描述
L 公司有 n 个工厂,由高到低分布在一座山上,工厂 1 在山顶,工厂 n 在山脚。
由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。突然有一天,L 公司的总裁 L 先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是 L 先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。
由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第 i 个工厂目前已有成品 pi 件,在第 i 个工厂位置建立仓库的费用是 ci。
对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于 L 公司产品的对外销售处设置在山脚的工厂 n,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,一件产品运送一个单位距离的费用是 1。
假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到以下数据:
- 工厂 i 距离工厂 1 的距离 xi(其中 x1=0)。
- 工厂 i 目前已有成品数量 pi。
- 在工厂 i 建立仓库的费用 ci。
请你帮助 L 公司寻找一个仓库建设的方案,使得总的费用(建造费用 + 运输费用)最小。
输入格式
输入的第一行是一个整数 n,代表工厂的个数。
第 2 到 (n + 1) 行,每行有三个用空格隔开的整数,第 (i + 1) 行的整数依次代表 xi, pi, ci。
输出格式
仅输出一行一个整数,代表最优方案的费用。
输入输出样例
输入 #1
3
0 5 10
5 3 100
9 6 10
输出 #1
32
说明/提示
样例输入输出 1 解释
在工厂 1 和工厂 3 建立仓库,建立费用为 10+10=20 ,运输费用为 (9−5)×3=12,总费用 32。
数据范围与约定
对于 20% 的数据,保证 n≤500。
对于 40% 的数据,保证 n≤104。
对于 100% 的数据,保证 1≤n≤106,0≤xi,pi,ci<231。
对于任意的 1≤i<n,保证 xi<xi+1。
设答案为 ans,保证 ans+ (i=1)∑(n) pixi<263 。
f[i]:在i处修建仓库的最小费用
则有:
f[i] = min{ f[j] + x[i] * sigma(p[l]) - sigma(x[l]*p[l]) } + c[i]
(0<=j<i) (j+1<=l<=i) (j+1<=l<=i)
用前缀和优化,令sump[i]=sigma( p[i] ),sum[i]=sigma(x[i]*p[i]),则原始转化成:
(1<=i<=i) (1<=i<=i)
f[i] = min{ f[j] + x[i] * (sump[i] - sump[j]) - (sum[i] - sum[j]) } + c[i]
若决策j优于决策k,则
f[j] + x[i] * (sump[i] - sump[j]) - (sum[i] - sum[j])
< f[k] + x[i] * (sump[i] - sump[k]) - (sum[i] - sum[k])
两边一消化简
(f[j] + sum[j]) - (f[k] + sum[k]) < x[i] * (sump[j] - sump[k])
即
(f[j] + sum[j]) - (f[k] + sum[k])
--------------------------------- < x[i]
sump[j] - sump[k]
令 Y(x) = f[x] + sum[x] , X(x) = sump[x],则
Y(j) - Y(k)
----------- < x[i]
X(j) - X(k)
Code moo~~
#include<bits/stdc++.h>
#define Bessie moo~~
#define int long long
using namespace std;
const int N=10010006;
int n;
int x[N],p[N],c[N],sump[N],sum[N],f[N];
int q[N],t,h;
inline int Y(int a){return f[a]+sum[a];}
inline int X(int a){return sump[a];}
inline double slope(int j,int k){
return 1.0*(Y(j)-Y(k))/(X(j)-X(k));
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>x[i]>>p[i]>>c[i];
sump[i]=sump[i-1]+p[i];
sum[i]=sum[i-1]+x[i]*p[i];
}
for(int i=1;i<=n;i++){
while(h<t&&slope(q[h+1],q[h])<x[i])h++;//排除斜率小者
int j=q[h];
f[i]=f[j]+x[i]*(sump[i]-sump[j])-sum[i]+sum[j]+c[i];
while(h<t&&slope(q[t],q[t-1])>slope(i,q[t]))t--;//加入的话斜率更小更能往下
q[++t]=i;
}
cout<<f[n];
return 0;
}