BZOJ 1096 [ZJOI2007]仓库建设:斜率优化dp

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1096

题意:

  有n个工厂,从左往右排成一排,分别编号1到n。

  每个工厂里有p[i]件产品,到1号工厂的距离为x[i],在此处建一个仓库的花费为c[i]。

  现在你需要建造一些仓库,使得所有产品都被运送到仓库中来。

  产品只能从左往右运输。每一件产品运输一个单位距离的花费为1。

  问你最小总花费(运输费 + 建仓库花费)。

 

题解:

  表示状态:

    dp[i]表示在工厂i建了一个仓库,并且仓库1 to i中的产品都已经被运到仓库中了,此时的最小总花费。

  找出答案:

    ans = dp[n]

    因为无论如何都必须在工厂n处建一个仓库。

  如何转移:

    dp[i] = min dp[j] + ∑(p[k]*(x[i]-x[k])) + c[i]

    (j < i, k = j+1 to i)

    定义前缀和数组:sp[i] = ∑ p[j], s[i] = ∑(x[j]*p[j])

    (j = 1 to i)

    则原转移方程可化简为:

      dp[i] = min dp[j] + x[i]*(sp[i]-sp[j]) - s[i] + s[j] + c[i]

  边界条件:

    dp[0] = 0

  斜率优化:

    设j < k,且k的决策更优。

    则有:dp[j] + x[i]*(sp[i]-sp[j]) + s[j]> dp[k] + x[i]*(sp[i]-sp[k]) + s[k]

    整理得:(dp[j]+s[j]-dp[k]-s[k]) / (sp[j]-sp[k]) < x[i]

    (注意:因为sp[j]-sp[k]<0,所以除过来之后不等式要变号)

    所以slope(i,j) = (dp[i]+s[i]-dp[j]-s[j]) / (sp[i]-sp[j])

    由于x[i]递增,所以用单调队列维护下凸壳即可。

 

AC Code:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define MAX_N 1000005
 5 #define INF 1000000000
 6 
 7 using namespace std;
 8 
 9 int n;
10 int q[MAX_N];
11 long long x[MAX_N];
12 long long p[MAX_N];
13 long long c[MAX_N];
14 long long s[MAX_N];
15 long long dp[MAX_N];
16 
17 void read()
18 {
19     cin>>n;
20     memset(s,0,sizeof(s));
21     memset(p,0,sizeof(p));
22     for(int i=1;i<=n;i++)
23     {
24         cin>>x[i]>>p[i]>>c[i];
25         s[i]=s[i-1]+x[i]*p[i];
26         p[i]+=p[i-1];
27     }
28 }
29 
30 inline double slope(int i,int j)
31 {
32     return (double)(dp[i]+s[i]-dp[j]-s[j])/(p[i]-p[j]);
33 }
34 
35 void work()
36 {
37     int l=1,r=1;
38     q[1]=0,dp[0]=0;
39     for(int i=1;i<=n;i++)
40     {
41         while(l<r && slope(q[l],q[l+1])<=x[i]) l++;
42         dp[i]=dp[q[l]]+x[i]*(p[i]-p[q[l]])-s[i]+s[q[l]]+c[i];
43         while(l<r && slope(q[r],i)<slope(q[r-1],q[r])) r--;
44         q[++r]=i;
45     }
46     cout<<dp[n]<<endl;
47 }
48 
49 int main()
50 {
51     read();
52     work();
53 }

 

转载于:https://www.cnblogs.com/Leohh/p/8407247.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值