洛谷 P2120 [ZJOI2007]仓库建设
Step1
朴素DP
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
long long x=0;
for(int k=j+1;k<=i;k++) x+=p[k]*(a[i]-a[k]);
dp[i]=min(dp[i],dp[j]+x+c[i]);
}
}
Step 2
前缀和优化
for(int i=1;i<=n;i++) s1[i]=s1[i-1]+p[i],s2[i]=s2[i-1]+p[i]*a[i];
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
dp[i]=min(dp[i],dp[j]+a[i]*(s1[i]-s1[j])-(s2[i]-s2[j])+c[i]);
}
}
Step 3
单调队列优化
/*
dp[i]=min(dp[i],dp[j]+a[i]*(s1[i]-s1[j])-(s2[i]-s2[j])+c[i]);
j<k&& k is better
dp[j]+a[i]*(s1[i]-s1[j])-(s2[i]-s2[j])+c[i]>dp[k]+a[i]*(s1[i]-s1[k])-(s2[i]-s2[k])+c[i]
dp[j]+a[i]*s1[i]-a[i]*s1[j]-s2[i]+s2[j]+c[i]>dp[k]+a[i]*s1[i]-a[i]*s1[k]-s2[i]+s2[k]+c[i]
dp[j]-a[i]*s1[j]+s2[j]>dp[k]-a[i]*s1[k]+s2[k]
(dp[j]+s2[j])-(dp[k]+s2[k])>a[i]*(s1[j]-s1[k])
(dp[j]+s2[j])-(dp[k]+s2[k])/(s1[j]-s1[k])>a[i]
(dp[k]+s2[k])-(dp[j]+s2[j])/(s1[k]-s1[j])>a[i]
a[i]递增
斜率也要递增
单调队列维护
*/
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0; char c=getchar(); int flag=1;
while(!isdigit(c)) { if(c=='-') flag=-1; c=getchar(); }
while(isdigit(c)) { x=((x+(x<<2))<<1)+(c^48); c=getchar(); }
return x*flag;
}
const int N=1e6+50;
int n;
long long a[N],p[N],c[N];
long long s1[N],s2[N];
long long dp[N];
int q[N];
int l,r;
signed main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read(),p[i]=read(),c[i]=read();
dp[i]=1e18;
}
for(int i=1;i<=n;i++) s1[i]=s1[i-1]+p[i],s2[i]=s2[i-1]+p[i]*a[i];
l=r=1; q[1]=0;
for(int i=1;i<=n;i++){
while(r>l){
if((dp[q[l+1]]+s2[q[l+1]]-dp[q[l]]-s2[q[l]])<a[i]*(s1[q[l+1]]-s1[q[l]])) ++l;//当前的队头斜率小于a[i],即队头无法对答案有贡献,弹出
else break;
}
dp[i]=min(dp[i],dp[q[l]]+a[i]*(s1[i]-s1[q[l]])-(s2[i]-s2[q[l]])+c[i]);
while(r>l){
if((dp[q[r]]+s2[q[r]]-dp[q[r-1]]-s2[q[r-1]])*(s1[i]-s1[q[r]])>=(dp[i]+s2[i]-dp[q[r]]-s2[q[r]])*(s1[q[r]]-s1[q[r-1]])) --r;//维护队尾斜率递增
else break;
}
q[++r]=i;
}
printf("%lld\n",dp[n]);
return 0;
}