bzoj 1096: [ZJOI2007]仓库建设(斜率DP)

来源:互联网 发布:zoom会议软件注册 编辑:程序博客网 时间:2024/05/18 13:45

1096: [ZJOI2007]仓库建设

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 5232  Solved: 2324
[Submit][Status][Discuss]

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


设dp[i]表示处理完前i个工厂的最小花费


先往最暴力的方法想,可以得出


其中c[y]表示在第y个工厂建造仓库的花费,len[y]表示第1个工厂与第y个工厂之间的距离,sum[y]表示存货量

复杂度O(n^3)


其中很显然可以求前缀和,不过len[]里面的都是变量,要转一下公式得


这个公式相当于dp[y] = min(dp[x]+所有物品都从0开始运到y的费用减去所有物品从0送到它所在工厂的费用)+c[y]

令a[i]为sum[i]的前缀和,b[i]为sum[i]*len[i]的前缀和,公式为


复杂度O(n²)


对于当前工厂y,如果在j点建工厂比在k点建工厂更优(当然中间都不建,且j>k)那么有


这样维护一个队列就好了,假设k是队尾,j是k前一个并且满足上式,那么将k弹掉

队头类似,计算的时候建议将(a[j]-a[k])除掉防止爆long long


#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define LL long longtypedef struct{LL x;LL val;LL pri;}Point;Point s[1000005];LL dp[1000005], sum[1000005], pri[1000005], q[1000005];double Jud(LL j, LL k){return 1.0*(dp[j]-dp[k]+pri[j]-pri[k])/(sum[j]-sum[k]);}int main(void){LL n, i, l, r;scanf("%lld", &n);for(i=1;i<=n;i++){scanf("%lld%lld%lld", &s[i].x, &s[i].val, &s[i].pri);sum[i] = sum[i-1]+s[i].val;pri[i] = pri[i-1]+s[i].val*s[i].x;}memset(dp, 62, sizeof(dp));dp[0] = 0;l = r = 1;for(i=1;i<=n;i++){while(l<r && Jud(q[l+1], q[l])<s[i].x)l++;dp[i] = dp[q[l]]+(sum[i]-sum[q[l]])*s[i].x-(pri[i]-pri[q[l]])+s[i].pri;while(l<r && Jud(i, q[r])<Jud(q[r], q[r-1]))  r--;q[++r] = i;}printf("%lld\n", dp[n]);return 0;}

原创粉丝点击