爲了學習單調隊列優化DP奔向了此題。。。學習
基礎的多重揹包就不展開了。設\(f_{i,j}\)爲選前\(i\)個物品,重量不超過\(j\)的最大價值,\(w\)爲重量,\(v\)爲價值(蒟蒻有強迫症,特別不喜歡把\(v\)和\(w\)反着搞,\(weight\)和\(value\)嘛!),直接給轉移方程優化
\[f_{i,j}=\max\{f_{i-1,j-kw_i}+kv_i\},k\in[0,min\{m,\lfloor \frac j{w_i}\rfloor\}]\]spa
顯然,\(f_i\)都是從\(f_{i-1}\)轉過來的,因此第一維能夠滾掉,獲得每次轉移的更簡化的方程.net
\[f_j=\max\{f_{j-kw}+kv\}\]code
這樣是\(O(nmW)\)的,仍是要想辦法優化。blog
衆所周知,DP優化的根本原則是去掉無用的狀態、利用重複轉移的狀態。但是這方程一眼根本看不出什麼可優化的地方啊。。。。。。隊列
咱們要像這位Dalao同樣善於發現,他的blogget
因此,無論這個想法是怎麼來的,咱們先把\(j\)按模\(i\)意義下分組,設\(j=k_1w+d\),那麼一組裏的\(d\)都是同一個值。io
而後方程就變成了這樣class
\[f_j=\max\{f_{k_1w+d-kw}+kv\}\]
\[\qquad\qquad\quad=\max\{f_{(k_1-k)w+d}-(k_1-k)v\}+k_1v\]
忽然看到了\(k_1-k\)的重複出現!這也就意味着,在每一組中,有意義的狀態只有\(\lfloor\frac{W-d}w\rfloor\)種!(\(W\)是最大載重)每次總的狀態也就只有\(O(W)\)了。
設\(g_k=f_{kw+d}-kv\)。那麼由於有\(m\)的限制,因此對於每一個\(k_1\),咱們須要且只能從\(max\{g_k|k\in[\max\{0,k_1-m\},k_1]\}\)轉移。對於這樣的轉移,能夠形象地和滑動窗口聯繫一下,至關於有一個寬度爲\(m\)的窗口從一邊一步步往另外一邊移動,每移一次都要取出窗口內的最大值。這個就上單調隊列維護。
首先枚舉\(d\)。接着,爲了方便滾動,咱們從大到小枚舉\(k\)和\(k_1\),用一個單調隊列維護下標在\([k_1-m,k_1]\)範圍內的依次遞減的若干個\(g\)值,由於顯然若是有\(g_x\geq g_y,x<y\)的話\(g_y\)是沒有用的。枚舉\(k_1\)時,每次隊首元素超出了範圍就把它出隊。用如今的隊首更新\(f_j\)即\(f_{k_1w+d}\)。接着下一個元素\(g_{k_1-m-1}\)要入隊了,把隊尾\(g\)比這個小的全出隊,再讓它進來。最後輸出\(f_W\)便可。
這樣就是\(O(nW)\)的了,比二進制拆分難理解些可是更優秀了。
結合代碼理解會更輕鬆哦
#include<cstdio> #define RG register #define R RG int #define G c=getchar() const int N=1e5+9; int f[N],g[N],q[N]; inline int in(){ RG char G; while(c<'-')G; R x=c&15;G; while(c>'-')x=x*10+(c&15),G; return x; } inline int max(R x,R y){return x>y?x:y;} inline void chkmx(R&x,R y){if(x<y)x=y;} int main(){ R n=in(),maxw=in(),maxk,lim,v,w,m,d,i,k,k1,h,t,now; for(i=1;i<=n;++i){ v=in();w=in();m=in(); for(d=0;d<w;++d){//枚舉餘數 maxk=(maxw-d)/w;lim=max(maxk-m,0);//先肯定最初的範圍 for(t=0,k=maxk-1;k>=lim;--k){//窗口先擴大寬度到m now=f[k*w+d]-k*v; while(t&&g[t]<=now)--t;//維護單調性 g[++t]=now;q[t]=k; } for(h=1,k1=maxk;~k1;--k1,--k){//能夠開始轉移了 if(h<=t&&q[h]>=k1)++h;//接着移動 if(h<=t)chkmx(f[k1*w+d],g[h]+k1*v);//轉移 if(k<0)continue;//注意窗口可能已經出正數範圍了 now=f[k*w+d]-k*v; while(h<=t&&g[t]<=now)--t;//維護單調性 g[++t]=now;q[t]=k; } } } printf("%d\n",f[maxw]); return 0; }