P3195-[HNOI2008]玩具装箱【斜率优化dp】
正題
題目鏈接:https://www.luogu.com.cn/problem/P3195
題目大意
nnn個物品,分成若干段,每一段的長度為j?i+∑i=lrCkj-i+\sum_{i=l}^rC_kj?i+∑i=lr?Ck?,打包價格為(長度?L)2(長度-L)^2(長度?L)2
求最小價格和。
解題思路
si=∑j=1iCjs_i=\sum_{j=1}^iC_jsi?=∑j=1i?Cj?
設fif_ifi?表示iii前面的都打包完了,有
fi=fj+(si?sj+i?j?1?L)2f_i=f_j+(s_i-s_j+i-j-1-L)^2fi?=fj?+(si??sj?+i?j?1?L)2
fi=fj+(si+i?sj?j?1?L)2f_i=f_j+(s_i+i-s_j-j-1-L)^2fi?=fj?+(si?+i?sj??j?1?L)2
然后dpdpdp可以做到O(n2)O(n^2)O(n2),因為有平方,考慮斜率優化
定義ai=si+i,bi=sj+j+1+La_i=s_i+i,b_i=s_j+j+1+Lai?=si?+i,bi?=sj?+j+1+L
有
fi=fj+(ai?bj)2f_i=f_{j}+(a_i-b_j)^2fi?=fj?+(ai??bj?)2
fi=fj+ai2?2aibj+bj2f_i=f_j+a_i^2-2a_ib_j+b_j^2fi?=fj?+ai2??2ai?bj?+bj2?
2aibj+fi?ai2=fj+bj22a_ib_j+f_i-a_i^2=f_j+b_j^22ai?bj?+fi??ai2?=fj?+bj2?
對于每一個jjj表示一個點(bj,fj+bj2)(b_j,f_j+b_j^2)(bj?,fj?+bj2?)(后文中稱之為決策點)
考慮如何使fif_ifi?最小,因為ai2a_{i}^2ai2?是定值即讓fi?ai2f_{i}-a_{i}^2fi??ai2?最小,那么問題就變為了一條y=2aix+ky=2a_ix+ky=2ai?x+k的直線,要求經過某個決策點使得kkk最小。
那么顯然,可能的點一定是一個下凸殼(相鄰的點斜率單調上升),而因為2ai2a_i2ai?這個斜率也是單調上升的,我們可以知道答案就是第一個決策點滿足與下一個決策點的斜率≥2ai\geq 2a_i≥2ai?。
那么我們維護一個單調隊列即可。
時間復雜度O(n)O(n)O(n)
codecodecode
#include<cstdio> #include<cstring> #include<algorithm> #define pow2(x) ((x)*(x)) using namespace std; const int N=5e4+10; struct node{double x,y;int num; }q[N]; int n,head,tail,L; double s[N],a[N],b[N],f[N]; double slope(node x,node y) {return ((y.y-x.y)/(y.x-x.x));} int main() {scanf("%d%d",&n,&L);for(int i=1;i<=n;i++){scanf("%lf",&s[i]);s[i]+=s[i-1];a[i]=s[i]+i;b[i]=s[i]+i+L+1;}b[0]=L+1;head=tail=1;q[1]=(node){b[0],b[0]*b[0],0};for(int i=1;i<=n;i++){while(head<tail&&slope(q[head],q[head+1])<2*a[i])head++;int p=q[head].num;f[i]=f[p]+pow2(a[i]-b[p]);node w=(node){b[i],f[i]+b[i]*b[i],i};while(head<tail&&slope(w,q[tail-1])<slope(q[tail-1],q[tail]))tail--;q[++tail]=w;}printf("%.0lf",f[n]); }總結
以上是生活随笔為你收集整理的P3195-[HNOI2008]玩具装箱【斜率优化dp】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2015台式电脑配置推荐(2015台式电
- 下一篇: P1975-[国家集训队]排队【树状数组