[bzoj 1096] [ZJOI2007]仓库建设

[bzoj 1096] [ZJOI2007]仓库建设

Time Limit: 10 Sec  Memory Limit: 162 MB

Submit: 5262  Solved: 2341

Description

  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公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。

Input

  第一行包含一个整数N,表示工厂的个数。接下来N行每行包含两个整数Xi, Pi, Ci, 意义如题中所述。

Output

  仅包含一个整数,为可以找到最优方案的费用。

Sample Input

3
0 5 10
5 3 100
9 6 10

Sample Output

32

HINT

在工厂1和工厂3建立仓库,建立费用为10+10=20,运输费用为(9-5)*3 = 12,总费用32。如果仅在工厂3建立仓库,建立费用为10,运输费用为(9-0)*5+(9-5)*3=57,总费用67,不如前者优。

【数据规模】

对于100%的数据, N ≤1000000。 所有的Xi, Pi, Ci均在32位带符号整数以内,保证中间计算结果不超过64位带符号整数。 

 

继续斜率优化DP。

感觉很好啊~~~

轻松写出转移方程:fi=min{fj+ci+sigma(k=j+1,i)pk*(xi-xk)}(j<i)

设s1i=sigma(j=1,i)pj,s2i=sigma(j=1,i)pj*xj

展开得到fi=fj+ci+s1ixi-s1jxi-s2i+s2j

设k<j且fj优于fk,则有xi>((fj+s2j)-(fk+s2k))/(s1j-s1k)。

然后可以稍微简化一下原式,方便观察分类——

设Ci=ci+s1ixi-s2i,Di=s2i

fi=fj+Ci+Dj-xis1j,这样就方便了处理。

code:(假装是已经掌握了一类斜率DP的真谛)

 1 %:pragma GCC optimize(2)
 2 #include<bits/stdc++.h>
 3 #define sqr(x) ((x)*(x))
 4 #define LL long long
 5 using namespace std;
 6 const int N=1000005;
 7 const double inf=1e18;
 8 int n,k,l,r; LL X[N],P[N],C[N],s1[N],s2[N],f[N];
 9 struct point {
10     LL x,y;
11     point() {}
12     point(LL _x,LL _y):x(_x),y(_y) {}
13 }st[N];
14 int read() {
15     int x=0,f=1; char ch=getchar();
16     while (ch<'0'||ch>'9') f=(ch=='-')?-1:1,ch=getchar();
17     while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
18     return x*f;
19 }
20 double slope(point u,point v) {
21     return u.x==v.x?(u.y<v.y?inf:-inf):1.0*(v.y-u.y)/(v.x-u.x);
22 }
23 LL get(LL k) {
24     while (l<r&&slope(st[l],st[l+1])<1.0*k) l++;
25     return st[l].y-k*st[l].x;
26 }
27 void insert(point cur) {
28     while (l<r&&slope(st[r-1],st[r])>slope(st[r-1],cur)) r--;
29     st[++r]=cur;
30 }
31 int main() {
32     n=read(),s1[0]=s2[0]=0;
33     for (int i=1; i<=n; i++) X[i]=read(),P[i]=read(),C[i]=read();
34     for (int i=1; i<=n; i++) s1[i]=s1[i-1]+P[i],C[i]+=s1[i]*X[i];
35     for (int i=1; i<=n; i++) s2[i]=s2[i-1]+P[i]*X[i],C[i]-=s2[i];
36     l=1,r=0,st[++r]=point(0,0);
37     for (int i=1; i<=n; i++) {
38         f[i]=get(X[i])+C[i];
39         insert(point(s1[i],f[i]+s2[i]));
40     }
41     printf("%lld\n",f[n]);
42     return 0;
43 }
View Code

 

 

 

转载于:https://www.cnblogs.com/whc200305/p/7660083.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值