前言:揹包學了無數遍,這次總算能記住點東西了qaq
揹包是線性dp中一類重要而特殊的模型。
0/1揹包
給定$n$個物品,其中第$i$個物品的體積為$w_$,價值為$c_$。現在給你乙個體積為$m$的揹包,問怎樣選擇使得物品總價值最大?
我們先考慮樸素演算法。
設$f[i][j]$為已經考慮了前$i$個物品,選出總體積為$j$的物品放入揹包所取得的最大價值。
如果不選第$i$件物品,則$f[i][j]=f[i-1][j]$。
如果選第$i$件物品,則$f[i][j]=f[i-1][j-w_]+c_ (j\geq w_)$。兩者中取最大值。
考慮優化:因為在考慮第$i$件物品時只取決於考慮第$i-1$件物品時的情況,所以上面$f$陣列的第一維可以省去。
即$f[j]=max(f[j],f[j-w[i]]+c[i])$。
請注意:$j$是倒序迴圈的。假設正序迴圈:比如用$j-2w_$的情況來更新$j-w$,此時$j-w$已經過度到第$i$個階段,再迴圈到$j-w$時,就會發生「第$i$個階段更新第$i$個階段」的情況,違背了線性dp的原則。而倒序迴圈保證了每個物品只會被考慮一次,符合線性dp的原則。
完全揹包
和0/1揹包差不多,只是增添了乙個條件:每個物品可以被挑選無數次。
和0/1揹包唯一不同的地方:$j$是正序迴圈的。因為每個物品都可以被選無數次。
$f[j]=max(f[j],f[j-w_]+c_$。
多重揹包
和完全揹包的條件不同:每個物品可以被選$t_$次。
最樸素的方法當然是從$0$到$t_$迴圈,每種情況都試一遍。但複雜度較高。
我們可以用二進位制拆分法。眾所周知,$2^0,2^1,2^2,...,2^k-1$可以表示$0$到$2^k -1$的所有整數。所以我們可以用這種方法把物品二進位制拆分。設$r_=c_-2^0-2^1-...-2^p$,則可以拆分為$p+2$個物品,體積分別為$2^0 *w_,2^1 *w_,...,2^p *w_,r_*w_$。此方法將物品拆分為$logc_$個,效率較高。
分組揹包
條件變化:有$i$組物品,每組物品有$k$個,每組最多選乙個物品,求最大價值。
直接讓$k$迴圈選取即可,只要選完了就立即從第$i$個階段過渡到第$i+1$個狀態。
for (int i=1;i<=n;i++)for (int j=m;j>=0;j--)
for (int k=1;k<=c[i];k++)
if (j>=v[i][k]) f[j]=max(f[j],f[i-v[i][k])+w[i][k]
注意$k$迴圈要在$j$迴圈之內。
揹包問題學習筆記
有n件物品和乙個容量為v的揹包。第i件物品的費用是c i 價值是w i 求解將哪些物品裝入揹包可使價值總和最大。從這個題目中可以看出,01揹包的特點就是 每種物品僅有一件,可以選擇放或不放。01揹包問題的狀態轉移方程是 f i v max其中,即fi表示前i件物品恰放入乙個容量為v的揹包可以獲得的最...
揹包再學習筆記
之前學習的幾個揹包都是背幾個一維陣列的板子,沒有深入的理解其中的含義,當碰到乙個相似的揹包的問題時,板子出現了短板,這時就難於寫出題目 先說 01 揹包 dp i j max dp i 1 j dp i 1 j v i w i 這裡的 i 表示第幾個物品,j表示目前用的揹包體積 還有乙個要注意的是揹...
樹形揹包學習筆記
給定一棵有 n 個節點的點權樹,要求你從中選出 m 個節點,使得這些選出的節點的點權和最大,乙個節點能被選當且僅當其父親節點被選中,根節點可以直接選。考慮設 f u i 表示在 u 的子樹中選擇 i 個節點 包括它本身 的最大貢獻,則可列出以下轉移方程。f u i max f u j f v i j...