1096: [ZJOI2007]仓库建设
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 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
Sample Output
设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 long
typedef 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;
}