揹包九講 簡單揹包

2022-03-20 05:30:30 字數 3809 閱讀 3263

揹包問題是一種動態規劃演算法的衍生問題。它可以被看作一種獨立的題型,也可以看作是一種線性動態規劃。學好揹包、學會揹包,對於深入理解動態規劃演算法有著極大的好處,並能幫助理解一些更深層次的動態規劃問題。

那麼就開始吧~

題目型別

有\(n\)件物品和乙個容量為\(v\)的揹包。第\(i\)件物品的費體積是\(v[i]\),價值是\(val[i]\),求將哪些物品裝入揹包可使價值總和最大。

問題解析

0/1揹包問題是最基礎的揹包問題,可以這麼說:0/1揹包是所有揹包的祖先。因為0/1揹包是用線性動歸的思路來求解的,而其他的揹包是用0/1揹包的思想來求解的。所以,為了解決這個問題,我們來用線性dp的基本內容來思考。

因為是動態規劃,所以我們在思考的時候一定要牢記dp的兩個性質:無後效性和最優子結構,也就是說,設定決策是解決動歸的靈魂。回歸0/1揹包的問題,我們容易得出乙個性質:每件物品只會面臨乙個決策:放還是不放。那麼,我們接下來的部分就可以用這兩個選擇來繼續進行。

狀態的定義建立在子問題的基礎上,針對0/1揹包,我們的狀態被設定成:\(dp[i] [j]\)表示前\(i\)件物品中任選若干件放入容量為\(j\)的揹包所能得到的最大價值。

\[dp[i] [j]=max(dp[i-1] [j],dp[i-1] [j-v[i]]+val[i])

\]我們有必要好好理解這個方程。這個方程表示的是乙個決策的過程,因為我們在決策一件物品放或不放的時候,牽扯到的資料只是在放它之前的那個狀態。那麼易知:如果不放的話,那\(dp[i] [j]\)就是\(dp[i-1] [j]\),因為容量沒有變,如果放了的話,那麼就需要在原來的揹包中騰出乙個\(v[i]\)那麼大的空位,否則這個新物品將無處可放。當然,如果放了這個物品,那麼還需要加上\(val[i]\)。

綜上所述,最終要求的答案就是\(dp[n] [v]\)。

空間複雜度的優化

我們發現,上面的\(dp\)陣列是乙個二維陣列,那麼假如揹包最大容量和物品件數都特別大的時候,這樣的空間複雜度肯定會**。所以我們需要採取手段來優化它的空間複雜度。01揹包的空間複雜度優化方式有兩種:一種是滾動陣列優化,另一種是壓維優化。

我們會發現,0/1揹包的狀態轉移方程只用到了\(dp[i-1] [j]\)的資料。也就是說,我們在求取答案的時候,\(1-(i-2)\)的資料是啥用沒有的。那麼我們考慮不再儲存這些資料,而將陣列「滾動」起來,依次覆蓋上一次用不著的資料,這樣就可以把dp陣列的第一維只開2位,而完成0/1揹包的dp操作,大大地降低了空間複雜度。

我們來看一下壓維操作。

因為每種物品只有放或者不放這兩種可能,所以我們在處理的時候可以把存物品的那一維省略,直接用揹包容量來進行dp,更大地壓縮了空間複雜度。

實現的時候非常簡單:

for(int i=1;i<=n;i++)

for(int j=v;j>=v[i];j--)

dp[j]=maxn(dp[j],dp[j-v[i]]+w[i]);

細心的讀者可能已經注意到了,這裡的第二維\(j\)是從\(v\)到\(v[i]\)開始列舉的。這是為什麼呢?

我們的外層迴圈進行的是逐物品列舉,而內層迴圈只起到乙個作用:持續更新當前容量的價值最大值。所以狀態轉移方程的意義就是:當前物品的放與不放是否會對當前的答案產生影響。這就是0/1揹包的壓維優化了。

例題推薦

noip2005採藥

題目型別

有\(n\)種物品和乙個容量為\(v\)的揹包,每種物品有無限件可用。第\(i\)種物品的費體積是$ v[i]\(,價值是\)val[i]$,求將哪些物品裝入揹包可使價值總和最大。

問題解析

本問題可以看作是0/1揹包的加強版本,因為每種物品都有無限件可以選擇,這樣就會給我們0/1揹包求解的過程帶來許多麻煩。比如:它的決策過程已經不是取或不取兩種,而是取1件、2件、3件、不取等很多種。那麼我們仍然試著寫出狀態轉移方程,按照0/1揹包的思路,令\(dp[i] [j]\)表示取前\(i\)種物品放入乙個容量為\(j\)的揹包裡的最大值。即有:

\[dp[i][j]=max(dp[i-1][j-k\times v[i]]+k\times val[i])

\]\[(0\le k\times v[i]\le j)

\]我們容易發現這是乙個三重迴圈,在資料稍微大一點的情況下,時間複雜度就受不了了。所以,我們必須對以上思路進行優化。

完全揹包到0/1揹包的轉化

在講解0/1揹包的時候說過,0/1揹包是最基本的揹包。不僅是完全揹包,之後的許多揹包都要轉換成0/1揹包進行求解。

在完全揹包問題中,有乙個顯然的性質:每種物品最多選擇\(v/v[i]\)件,於是我們把這種物品拆成\(v/v[i]\)個體積、價值均相等的物品,這樣就轉化成了0/1揹包問題。可以使用0/1揹包問題的轉移來求解。

倍增優化

完全揹包的接替模型其實是直接轉換成0/1揹包,因為總體積是\(v\),對於每種種類,最多只能放\(v/v[i]\)個物品,就可以直接轉換成0/1揹包。

但是這個轉換很麻煩,如果再放到一維陣列,很容易**,所以我們使用倍增思想進行優化。倍增思想是乙個很有用的東西,我們可以把剛才的\(n\)個滿足\(v/v[i]\)種類的物品給壓縮,怎麼做呢?

舉個例子: \(v=22,v[i]=3,w[i]=2;\)

用剛才說的\(v/v[i]\)就拆成了\(7\)個相同的物品。

用倍增優化呢?

我們就可以拆成空間為\(v[i]\times 2^k\),價值為\(w[i]\times 2^k\)的若干件物品,其中\(k\)滿足小於\(v\)的要求。

所以用這種方法,我們就一共拆出來了3件物品,僅僅三件!!!

體積為3,6,12,價值為2,4,8。

這也是完全揹包最常用的模板。

**如下:

for(int i=1;i<=n;i++)

for(int j=v[i];j<=v;j++)

dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

細心的讀者又會發現:這個模板和0/1揹包的模板只有第二層迴圈的迴圈次序不一樣。那麼為什麼上面的是0/1揹包,這個就變成了完全揹包呢?這是因為我們在完全揹包中要保證每乙個子問題的狀態是由它的前乙個狀態遞推而來的(0/1揹包拆分之後,每種物品選不選,選幾個,都是影響後續決策的),所以我們要正著推。而0/1揹包則正好相反,我們的每個物品的數值是確定的,為了符合無後效性,顯然要從後往前遞推。這其實也是完全揹包和0/1揹包本質上的區別。

例題推薦

usacoscore inflation

題目型別

有\(n\)種物品和乙個容量為\(v\)的揹包,每種物品有\(p[i]\)件可用。第\(i\)種物品的費體積是$ v[i]\(,價值是\)val[i]$,求將哪些物品裝入揹包可使價值總和最大。

問題解析

一種好想的基本方法是直接轉化成0/1揹包求解,因為題意已經告訴我們每種物品的件數,所以我們可以直接把它拆成\(p[i]\)個相同的物品,然後進行求解。

例題推薦

洛谷p1776寶物篩選

題目型別

前面三個揹包的混合版。

問題解析

原講解裡有一句話讓我印象深刻:所有的複雜問題都是由一堆簡單問題拆分而成的。那麼,我們解決這個問題的時候也應該把問題拆分成若干的簡單問題,然後進行求解。

這種結合有兩種物品,一種只能用一次,一種能用若干次。那麼根據題目中的意思,我們可以分別處理兩種不同的問題,即分別解決兩種物品的揹包。(**實現就是順逆序迴圈的靈活呼叫)

思路和以上的差不多,也是拆分求解。如果是多重揹包,就用單調佇列來求,複雜度很優。

例題推薦

洛谷p1833櫻花

揹包九講之 01揹包

01揹包是最基礎的揹包問題,其中01代表的就是第i個物品的選或不選,在此先設v i 為體積,w i 為價值。很顯然,我們可以使用二位陣列dp i j 來表示前i個物品在揹包容量為j的時候可存放的最大價值。首先dp 0 0 0是很顯然的。而計算dp i j 時,存在01兩種情況 選或不選第i件物品。1...

揹包九講之 完全揹包

完全揹包與01揹包的區別在於完全揹包第i件物品可以在允許範圍內選無數個,同樣的v i 表示體積,w i 表示價值。同樣的,我們先從二維入手,較為顯而易見。dp i j 表示前i件物品在容量為j的最大價值,那麼相應的也會出現很多情況 1.不選 dp i j dp i 1 j 2.選一件 dp i j ...

揹包九講 01揹包問題

1 01揹包問題描述 已知 有 n 件物品和乙個容量為 v 的揹包。第i件物品的重量為w i 得到的價值是 c i 問題 求解將哪些物品裝入揹包可使價值總和最大。條件 每種物品只有一件,可以選擇放或者不放 2 基本思路 01揹包的特點 每種物品只有一件,可以選擇放或者不放 子問題定義狀態f i v ...