1. 程式人生 > >【筆記篇】斜率優化dp(四) ZJOI2007倉庫建設

【筆記篇】斜率優化dp(四) ZJOI2007倉庫建設

描述 get -- ons turn clu 最小花費 ont inline

傳送門戳這裏>>>

\(n\leq1e6\), 顯然還是\(O(n)\)的做法.
這個題有個條件是只能運往編號更大的工廠的倉庫, 這也是寫出樸素dp的方程的條件.
我們令\(f[i]\)表示前\(i\)個工廠的最小花費, 那麽易得
\[f[i]=min\{f[j]+t(j,i)\}\]
其中這個\(t(j,i)\)表示將\((j,i)\)這個區間的東西運到\(i\)的總費用. 很顯然, 這個式子要\(O(1)\)求出來才行, 不然復雜度就要炸...
那麽怎麽\(O(1)\)求呢?
考慮類似於前綴和的性質.
技術分享圖片
我們令\(s_i\)為將\((1,i]\)這個區間中所有工廠的產品運到\(i\)

的總花費, \(c_i\)表示前\(i\)個工廠的產品總量, \(d_i\)表示第\(i\)個工廠的坐標, 我們發現, 如果對\(i,j\)做一波前綴和相減, 那麽前\(j\)個點的貨物都被多運了\(d_i-d_j\)的距離... 所以就可以推出
\[t(j,i)=s_i-s_j-c_j*(d_i-d_j)\]
這樣就可以扔進狀態轉移方程進行斜率優化了... 化完之後的式子是:
\(f[j]-s[j]+c[j]*d[j]\)=\(d[i]\)\(c[j]\)+\(f[i]-s[i]-w[i]\)
然後求的是最小值, 斜率還遞增(這好像是最常見的一種了吧?), 那就跟之前一樣咯= =
然而還是把演草紙上\(d[i],c[j]\)
的數組名抄反了WA了一次 但為什麽可以過樣例啊QAQ

然後就是沒有壓行的代碼: (簡單的斜率優化似乎總可以寫成標準的20行?

#include <cstdio>
const int N=1e6+6;
typedef long long LL;
LL f[N],s[N],c[N];
int q[N],w[N],d[N],n,h,t;
inline int gn(int a=0,char c=0){
    for(;c<'0'||c>'9';c=getchar());
    for(;c>47&&c<58;c=getchar())a=a*10
+c-48;return a; } double slope(int x,int y){ return 1.0*(f[x]-s[x]+c[x]*d[x]-f[y]+s[y]-c[y]*d[y])/(c[x]-c[y]); } int main(){ n=gn(); for(int i=1;i<=n;++i){ d[i]=gn();c[i]=c[i-1]+gn();w[i]=gn(); s[i]=s[i-1]+c[i-1]*(d[i]-d[i-1]); } for(int i=1,j;i<=n;++i){ while(h<t&&slope(q[h],q[h+1])<=d[i]) ++h; j=q[h]; f[i]=f[j]+s[i]-s[j]-c[j]*(d[i]-d[j])+w[i]; while(h<t&&slope(q[t],q[t-1])>=slope(q[t],i)) --t; q[++t]=i; } printf("%lld\n",f[n]); }

【筆記篇】斜率優化dp(四) ZJOI2007倉庫建設