注意到我们所有的东西都只能转移到后面,那么可以考虑dp
用dp[i]表示以i结尾建立仓库的最小花费
那么dp[i] = min(dp[i], dp[j] + w[i] + sigma(dis[i] - dis[k]) * num[k])
这个dp直接转移是n ^ 2
考虑优化这个dp
然后他是由前面所有的转移过来,而且还有其他的数组来计算答案,一般的数据结构解决不了这个问题,那么就xjb划式子搞搞,然后就可以推出一个东西
用sumd[i] 表示num[i] * dis[i]的前缀和
用sumn[i]表示num[i]的前缀和
考虑一个k比j更优秀,那么可以推出 dis[i] > (dp[k] + sumd[k] - dp[j] - sumd[j]) / (sumn[k] - sumn[j])
然后就是斜率优化的惯用套路
每次新加入一个的时候看看队首是否合法,把不合法的全部弹掉,然后更新dp值
例如 while (head < tail && slope(q[head], q[head + 1]) < 1.0 * dis[i]) head++;
然后得到dp[i]的值
再看看当前值是否能比队尾值,把废物搞掉
例如 while (head < tail && slope(q[tail - 1], q[tail]) > slope(q[tail], i)) tail--;
然后把这个B加入队列中
斜率优化主要是要看的出来,然后能推出式子,最重要的是要能划出一个斜率的形式,例如y[i] - y[j] / x[i] - x[j]这种
代码如下
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int readint() {
int rt = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
rt = (rt << 1) + (rt << 3) + ch - '0';
ch = getchar();
}
return rt * f;
}
ll readll() {
ll rt = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
rt = (rt << 1) + (rt << 3) + ch - '0';
ch = getchar();
}
return rt * f;
}
const int maxn = 1e6 + 5;
int n;
ll num[maxn], dis[maxn], w[maxn], sumd[maxn], sumn[maxn], dp[maxn];
int q[maxn << 1];
double slope(int k, int j) {
return 1.0 * (dp[k] + sumd[k] - dp[j] - sumd[j]) / (1.0 * sumn[k] - sumn[j]);
}
int main() {
n = readint();
for (int i = 1; i <= n; i++) {
dis[i] = readll(), num[i] = readll(), w[i] = readll();
}
for (int i = 1; i <= n; i++) {
sumd[i] = sumd[i - 1] + num[i] * dis[i];
sumn[i] = sumn[i - 1] + num[i];
}
int head = 1, tail = 0;
q[++tail] = 0;
for (int i = 1; i <= n; i++) {
while (head < tail && slope(q[head], q[head + 1]) < 1.0 * dis[i]) head++;
dp[i] = dp[q[head]] + dis[i] * (sumn[i] - sumn[q[head]]) - sumd[i] + sumd[q[head]] + w[i];
while (head < tail && slope(q[tail - 1], q[tail]) > slope(q[tail], i)) tail--;
q[++tail] = i;
}
printf("%lld\n", dp[n]);
return 0;
}