朴素方程:
f[i]=Min(f[j]+(sum[i]−sum[j])∗x[i]−(pre[i]−pre[j]))+c[i]
其中,
sum[i]
表示
pi
的前缀和,
pre[i]
表示
pi∗xi
的前缀和
假设这一段的物品从起点运出,得到花费为
(sum[i]−sum[j])∗x[i]
,而起点不在0处,那么对于物品
pi
可以少运
xi
的距离,那么就应该减去
∑ik=j+1pi∗xi
时间复杂度 O(N2)
考虑优化,假设
j<k
并且
k
比
那么得到不等式
f[k]+(sum[i]−sum[k]∗x[i]−(pre[i]−pre[k]))≤f[j]+(sum[i]−sum[j]∗x[i]−(pre[i]−pre[j]))
化简得到
f[j]+b[j]−(f[k]+b[k])sum[j]−sum[k]≤x[i]
设 slope(k,j)=f[j]+b[j]−(f[k]+b[k])sum[j]−sum[k]
剩下的,就是单调队列优化,如果队头元素满足
slope(que[l+1],que[l])
那么队头元素弹出,如果队尾元素满足
slope(i,que[r])<slope(que[r],que[r−1])
,队尾元素弹出
时间复杂度 O(N)
#include <cstdio>
using namespace std;
typedef long long LL;
typedef double dl;
const int SN = 1000000 + 10;
LL f[SN], sum[SN], x[SN], n, p[SN], c[SN], pre[SN];
LL que[SN], head, tail;
void Read(LL &x) {
LL in = 0,f = 1;char ch = getchar();
while(ch<'0' || ch>'9') {if(ch=='-') f = -1;ch = getchar();}
while(ch>='0' && ch<='9') {in = in*10+ch-'0'; ch = getchar();}
x = in*f;
}
LL get_1(int k, int j) {
return f[j]-f[k]+pre[j]-pre[k];
}
LL get_2(int k, int j) {
return sum[j]-sum[k];
}
dl get_3(int k, int j) {
return (double)(get_1(k,j)/(1.0*(get_2(k,j))));
}
int main() {
Read(n);
for(int i = 1; i <= n; i++) {
Read(x[i]),Read(p[i]),Read(c[i]);
sum[i] = p[i] + sum[i-1];
pre[i] = pre[i-1] + p[i]*x[i];
}
for(int i = 1; i <= n; i++) {
while(head < tail &&get_3(que[head+1], que[head]) <= x[i]) head++;
f[i] = f[que[head]] + c[i] + (sum[i]-sum[que[head]])*x[i] - (pre[i] - pre[que[head]]);
while(head < tail && get_3(que[tail], que[tail-1]) >= get_3(i, que[tail])) tail--;
que[++tail] = i;
}
printf("%lld\n",f[n]);
return 0;
}