1. 程式人生 > >2018.11.02【HNOI2008】【BZOJ1010】【洛谷P3195】玩具裝箱(斜率優化DP)

2018.11.02【HNOI2008】【BZOJ1010】【洛谷P3195】玩具裝箱(斜率優化DP)

洛谷傳送門

解析:

看到平方項多半就是兩種套路,決策單調性和斜率優化,這道題斜率優化可以O(n)O(n)

首先還是推DP式子,這個很好想(sumsum表示字首和)。fi=minj=1i1{(ij+sumisumjL1)2+fj}f_i=\min_{j=1}^{i-1}\{(i-j+sum_i-sum_j-L-1)^2+f_j\}

是在是太長了,代換一下,令A(i)=sumi+i,B(i)=A(i)+L+1A(i)=sum_i+i,B(i)=A(i)+L+1

B(i)=A(i)+L+1,則狀態轉移方程化簡為fi=minj=1i1{(A(i)B(j))2+fj}f_i=\min_{j=1}^{i-1}\{(A(i)-B(j))^2+f_j\}=A(i)2+minj=1i1{2A(i)B(j)+B(j)2+fj}=A(i)^2+\min_{j=1}^{i-1}\{-2A(i)B(j)+B(j)^2+f_j\}

然後每個決策點設定為(B(j),B(j)2+fj)(B(j),B(j)^2+f_j)

),B(j)2+fj),可以斜率優化了。

發現決策的斜率是單調的,單調佇列維護一下凸包就行了。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

cs int N=50004;
int q[N];
int head=1,tail=1;
ll n,L;
ll sum[N],f[N];

inline ll A(int i){return sum[i]
+i;} inline ll B(int i){return sum[i]+i+L+1;} inline ll calc(int i,int j){ return f[j]+(A(i)-B(j))*(A(i)-B(j)); } inline ll X(int i){return B(i);} inline ll Y(int i){return f[i]+B(i)*B(i);} inline double slope(int i,int j){ return (1.0*Y(i)-Y(j))/(1.0*X(i)-X(j)); } signed main(){ scanf("%lld%lld",&n,&L); for(int re i=1;i<=n;++i)scanf("%lld",&sum[i]),sum[i]+=sum[i-1]; for(int re i=1;i<=n;++i){ while(head<tail&&calc(i,q[head])>calc(i,q[head+1]))++head; f[i]=calc(i,q[head]); while(head<tail&&slope(i,q[tail-1])<slope(q[tail],q[tail-1]))--tail; q[++tail]=i; } printf("%lld",f[n]); return 0; }