YBTOJ洛谷P3195:玩具装箱(斜率优化dp)
傳送門
文章目錄
- 前言
- 解析
- 代碼
前言
斜率優化dp,就是利用斜率優化的dp
(逃)
解析
第一道斜優的題
分析題目
設sumisum_isumi?為1-i的c的前綴和
容易寫出dp轉移式:
dpi=min(dpj+(sumi?sumj+i?j?1?L)2)dp_i=min(dp_j+(sum_i-sum_j+i-j-1-L)^2)dpi?=min(dpj?+(sumi??sumj?+i?j?1?L)2)
但是平方轉移會T掉
考慮優化
設:
ai=sumi+ia_i=sum_i+iai?=sumi?+i
bi=sumi+i+1+Lb_i=sum_i+i+1+Lbi?=sumi?+i+1+L
注意到對于固定的 i,a和b都是可求的定值
那么上面的dp,就可以寫成:
dpi=min(dpj+(ai?bj)2)dp_i=min(dp_j+(a_i-b_j)^2)dpi?=min(dpj?+(ai??bj?)2)
把平方拆開:
dpi=min(dpj+ai2+bj2?2?ai?bj)dp_i=min(dp_j+a_i^2+b_j^2-2*a_i*b_j)dpi?=min(dpj?+ai2?+bj2??2?ai??bj?)
為了轉移優化,我們需要移一下項:
2?ai?bj+dpi?ai2=dpj+bj22*a_i*b_j+dp_i-a_i^2=dp_j+b_j^22?ai??bj?+dpi??ai2?=dpj?+bj2?
上面那個式子可以看成一個以bjb_jbj?為未知數,斜率為2?ai2*a_i2?ai?,且經過(bj,dpj+bj2)(b_j,dp_j+b_j^2)(bj?,dpj?+bj2?)的一次函數
沒明白?這么看:
f(x)=2?ai?x+dpi?ai2f(x)=2*a_i*x+dp_i-a_i^2f(x)=2?ai??x+dpi??ai2?
f(bj)=dpj+bj2f(b_j)=dp_j+b_j^2f(bj?)=dpj?+bj2?
dpidp_idpi?就是這個函數與y軸的截距加上aia_iai?的平方,因此我們實際上就是要使函數在y軸上的截距最小
有了這個函數之后,我們就可以開始嘗試優化了
對于每一個新的要求的dp[i],其直線對應的斜率是固定的
我們維護一個可以作為轉移點的隊列,其中的決策點對應的點對(b[x],dp[x]+b[x]^2)形成一個凸包的結構
由于隨著 i 的增大,其直線的斜率(2?ai2*a_i2?ai?)單調遞增,所以位于凸包上方的點是一定不會作為最優決策點的
若記A、B兩點之間的斜率為slope(A、B)
不難看出,要使其這條直線的y軸截距最小,我們應該找到**第一個
slope(P[j],P[j+1])>2?aislope(P[j],P[j+1])>2*a~i~slope(P[j],P[j+1])>2?a?i? 的位置
而且由于斜率單調遞增,Pj左側的點以后一定不會再被選到了
所以我們可以用一個單調隊列來維護
時間復雜度降為O(n)
代碼
#include<bits/stdc++.h> #define I register int using namespace std; #define ll long long const int N=5e4+10; ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f; } int n; ll sum[N],c[N],a[N],b[N],l,dp[N]; struct pos{ll x,y;int pl; }; pos q[N]; int st,ed; double slope(pos u,pos v){return 1.0*(v.y-u.y)/(v.x-u.x); } int main(){ // freopen("a.in","r",stdin); // freopen("a.out","w",stdout);n=read();l=read();for(int i=1;i<=n;i++){//printf("ok i=%d\n",i);c[i]=read();sum[i]=sum[i-1]+c[i];a[i]=sum[i]+i;b[i]=sum[i]+i+l+1;}b[0]=l+1;st=ed=1;q[1]={b[0],b[0]*b[0],0};for(int i=1;i<=n;i++){ll k=2*a[i];while(st<ed&&slope(q[st],q[st+1])<k) st++;ll x=q[st].x,y=q[st].y;int pl=q[st].pl;dp[i]=y+a[i]*a[i]-2*a[i]*b[pl];//printf("i=%d st=%lld dp=%lld k=%lld\n ",i,q[st].pl,dp[i],k);//for(int i=st;i<=ed;i++) printf("%d:(%lld %lld %lld) ",i,q[i].x,q[i].y,q[i].pl);//printf("\n\n");pos now=(pos){b[i],dp[i]+b[i]*b[i],i};while(st<ed&&slope(q[ed-1],q[ed])>slope(q[ed-1],now)){ed--;// printf("ou2!\n");}q[++ed]=now;}printf("%lld",(long long)dp[n]);return 0; } /* 6 10 5 8 5 10 19 1 */總結
以上是生活随笔為你收集整理的YBTOJ洛谷P3195:玩具装箱(斜率优化dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iQOO 12 Pro正式发布 堪称六边
- 下一篇: YBTOJ:消除格子(二分图匹配)