【算法複習】揹包問題 經典動態規劃

其實我大概想法是想寫一個動態規劃的專題,然後裏面可能有一些小專題,所以如果我沒有鴿掉的話,這篇的鏈接會放入DP專題裏面

這裏大概就寫寫常見的揹包問題吧。

01揹包

可以說是最經典的揹包問題了,有\(n\)個物品, 每個物品有一個價值\(vi\)和一個體積\(wi\),揹包的最多容納體積之和爲\(m\)的物體。
現在讓你求揹包可以放入的最高價值之和。
我們考慮令\(dp[i][j]\)表示考慮到第i個物品,現在已經佔用的揹包容積是j的最優價值,那麼我們有轉移方程:
\(dp[i][j] = max\{dp[i - 1][j - w[i]] + v[i]\}\)
僞代碼:

for i 1 to n
  for j 1 to m
    dp[i][j] = max{dp[i - 1][j - w[i]] + v[i]}

滾動數組優化

可以發現\(dp[i]\)一定從\(dp[i - 1]\)轉移,而且我們只需要最後的dp[n],也就是考慮完所有物品的數組.所以我們並不需要保留所有的dp數組,只需要保留上一組的信息就可以算出下一組,
因此我們令\(f[i]\)表示已經佔用的揹包容積是j的最優價值。
僞代碼:

for i 1 to n
  for j m to 1//注意這裏
    dp[j] = max{dp[j - w[i]] + v[i]}

這裏注意一個細節,你可以發現j這個循環的循環順序被反過來了,爲什麼呢?
考慮到w[i]爲正數,因此\(j - w[i] < j\),而我們的dp數組是需要用上一組的信息轉移出下一組,因此我們需要保證,求dp[j]用到的轉移狀態是上一組的狀態。
而求dp[j]時會用到比j小的狀態,因此我們需要保證在dp[j]時,比j小的狀態還沒有被更新,因此我們用逆序枚舉。
那麼爲什麼我們必須用上一組的信息轉移出下一組呢?
我們考慮如果我們用當前組轉移當前組會發生什麼。
可以發現,如果我們用當前組更新當前組,那就以爲着用於更新的狀態裏已經考慮過當前物品了,這個時候你再加一次當前物品,相當於考慮了多次當前物品,不符合每個物品只能用一次的條件。

完全揹包

\(n\)種物品, 每種物品有一個價值\(vi\)和一個體積\(wi\),且數量爲無限個,揹包的最多容納體積之和爲\(m\)的物體。
現在讓你求揹包可以放入的最高價值之和。
從01揹包的滾動數組優化的細節解釋中,可以發現,其實我們順序枚舉,雖然不滿足01揹包的條件限制,但是卻剛好滿足完全揹包的條件!
所以我們只需要將j順序枚舉,就是完全揹包的做法。
僞代碼:

for i 1 to n
  for j 1 to m//注意這裏
    dp[j] = max{dp[j - w[i]] + v[i]}

多重揹包

\(n\)種物品, 每種物品有一個價值\(vi\)和一個體積\(wi\),且數量爲\(ni\)個,揹包的最多容納體積之和爲\(m\)的物體。
現在讓你求揹包可以放入的最高價值之和。
有2個思路,1個是把有\(ni\)個的第i種物品,拆分成\(ni\)個,當01揹包做,1個是多一層循環,枚舉第i個物品放進去了多少個。
複雜度都是\(\sum{ni} \cdot m\)的。
其實這2種思路本質上差不多,我們考慮二進制拆分進行分組來優化。
對於第i個物品,其實我們只需要對\(ni\)進行拆分,使得拆分出來的幾個組,對於任意\(0<= x <= ni\),都有至少一種取捨方案使得第i種物品一共取走了x個.
要實現這個目標,二進制拆分是一個很好的選擇。
我們先找到一個最大的k使得\(2^k<=ni\),然後令\(t = ni - 2^k\),易知\(t < 2^k\)
然後我們對\(2^k\)進行拆分,拆成\(2^0 + 2^1 + 2^3 ... + 2^{k - 1} + 1\),容易證明這k個數可以組合出0到\(2^k\)中任意一個數。
然後對於剩下的t我們單獨作爲一組,由於\(t < 2^k\),所以之前的k個數可以組合出0到t中任意一個數,而\(2^{k} + 1\)\(ni\)中的任意一個數可以由t的這一組和之前的k組組合得到(你可以理解爲取走所有組,然後我們需要刪去一個0到t中的一個數來得到\(2^{k} + 1\)\(ni\)中的某個數,而這個數肯定能被前k個數湊出)

------以上是最基礎的三種揹包問題-----下面會寫一些擴展的揹包問題(大概就是差不多的問題+一點點改動)-------有時間就更------

恰好裝滿

其實可以直接套01揹包的做法,如果數據沒有價值爲0和負數的話(
然後看dp[m]有沒有值就行。
如果有價值爲0和負數……那就稍微改一下,令dp[i]表示考慮塞入體積之和爲i,能否實現。
然後初始化一下,dp[0] = 0, 其他都是-inf,
最後看dp[m]如果還是-inf的話,說明不能恰好裝滿,如果不是-inf的話那dp[m]就是答案

多個限制

直接dp數組加維就行

求方案數

改一下dp方程
以01揹包爲例:\(dp[j] += sum{dp[j - w[i]] + v[i]}\),注意這裏是+=,初始的dp[j]表示的是不放入當前物品,
完全揹包思路類似

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章