题目描述
L公司有N个工厂,由高到底分布在一座山上。
工厂1在山顶,工厂N在山脚。 由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。
突然有一天,L公司的总裁L先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是L先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。
由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第i个工厂目前已有成品Pi件,在第i个工厂位置建立仓库的费用是Ci。
对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于L公司产品的对外销售处设置在山脚的工厂N,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,假设一件产品运送1个单位距离的费用是1。
假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到以下数据:
- 工厂i距离工厂1的距离Xi(其中X1=0);
- 工厂i目前已有成品数量Pi;
- 在工厂i建立仓库的费用Ci;
请你帮助L公司寻找一个仓库建设的方案,使得总的费用(建造费用+运输费用)最小。
输入输出格式
输入格式:
第一行包含一个整数N,表示工厂的个数。接下来N行每行包含两个整数Xi, Pi, Ci, 意义如题中所述。
输出格式:
仅包含一个整数,为可以找到最优方案的费用。
输入输出样例
输入样例#1:
3
0 5 10
5 3 100
9 6 10
输出样例#1:
32
说明
在工厂1和工厂3建立仓库,建立费用为10+10=20,运输费用为(9-5)*3 = 12,总费用32。
如果仅在工厂3建立仓库,建立费用为10,运输费用为(9-0)*5+(9-5)*3=57,总费用67,不如前者优。
对于20%的数据, N ≤500;
对于40%的数据, N ≤10000;
对于100%的数据, N ≤1000000。 所有的Xi, Pi, Ci均在32位带符号整数以内,保证中间计算结果不超过64位带符号整数。
思路:
这是一道比较难的斜率优化变形题,我们可以设dp[i]
代表前i个工厂的最小费用,那么:
dp[i] = dp[j] + c[i] + [j+1, i]的所有工厂的p[k] * (x[i] - x[k])的和
我们可以设A[i]
代表前i个工厂的p[i]
和,B[i]
代表前i个工厂的p[i]*x[i]
和,那么化简上面的式子,得到:
dp[i] = dp[j] + c[i] + x[i] * (A[i] - A[j]) - (B[i] - B[j])
我们发现里边有不变量,变量以及混合值,所以这是一道典型的斜率优化问题
这时我们可以设k < j且j结果优于k,那么化简得:
dp[j] - x[i]A[j] + B[j] < dp[k] - x[i]A[k] + B[k]
移项得:
((dp[j] + B[j]) - (dp[k] + B[k])) / (A[j] - A[k]) < x[i]
我们设f[i] = dp[i] + B[i]
,代入得:
(f[j] - f[k]) / (A[j] - A[k]) < x[i]
我们就得到了斜率,剩下的步骤所有题都雷同不再多说,直接上代码
代码:
/*************************************************************************
> File Name: p.cpp
> Author: Zcy
> Mail: [email protected]
> Created Time: 三 1/23 18:16:17 2019
************************************************************************/
#include <stdio.h>
#define ll long long
#define N 1000000
ll x[N + 5] = {0};
ll c[N + 5] = {0};
ll A[N + 5] = {0};
ll B[N + 5] = {0};
ll f[N + 5] = {0};
ll dp[N + 5] = {0};
int q[N + 5] = {0};
int head = 0, tail = 0;
double get(int j, int k) {
return 1.0 * (f[j] - f[k]) / (A[j] - A[k]);
}
void set(int i, int j) {
dp[i] = dp[j] + c[i] + x[i] * (A[i] - A[j]) - (B[i] - B[j]);
f[i] = dp[i] + B[i];
}
int main () {
int n, p;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%d%lld", &x[i], &p, &c[i]);
A[i] = A[i - 1] + p;
B[i] = B[i - 1] + p * x[i];
}
q[tail++] = 0;
q[tail++] = 1;
set(1, 0);
for (int i = 2; i <= n; i++) {
while(tail - head > 1 && get(q[head + 1], q[head]) <= x[i]) {
head++;
}
set(i, q[head]);
while(tail - head > 1 && get(i, q[tail - 1]) <= get(q[tail - 1], q[tail - 2])) {
tail--;
}
q[tail++] = i;
}
printf("%lld\n", dp[n]);
return 0;
}
如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢