揹包問題主要是分為三種:0-1揹包,完全揹包,多重揹包
1:0-1揹包
定義:
何謂0-1揹包,可以這樣想,那裡有一堆值錢的東西,每一樣東西只有一件,他們的價值和體積都不一樣,現在要你從這n件裡面挑選一些放到乙個容量一定的揹包裡面,使得你的揹包裡的東西總價值最大。對於這些東西的每一件,你可以選擇放進你的揹包或者是不放進去。(這裡放與不放就對應著兩種狀態0,1),所以稱之為0-1揹包。
解法:
為什麼我們要選用動態規劃的思想呢?
考慮到運用dp(動態規劃)的幾個步驟
1. 描述乙個最優解的結構;
2. 遞迴地定義最優解的值;
3. 以「自底向上」的方式計算最優解的值;
4. 從已計算的資訊中構建出最優解的路徑
其中第1-3點是動態規劃求解問題的基礎,若題目只求最優解,則第四點可以省略。(以上幾點都是演算法導論上面的原話)
為了下面講解方便,先我們規定f[i][v]來表示前i件物品放入到容量為v的揹包裡所獲得的最大總價值,c[i]為第i件物品的體積,w[i]表示第i件物品的價值
現在對於這個問題,第i件物品不放或是放,則有兩種狀態,
第一種是不放,則f[i][v] = f[i-1][v] ;
第二種是放,則f[i][v]=f[i-1][v-c[i]]+w[i] ;
那麼我們取哪個呢?當然是這樣的 f[i][v] = max(f[i-1][v],f[i-1][v-c[i]]+w[i]) ;(dd大牛的揹包九講上說這個方程很重要,幾乎所有的揹包問題都是從這個方程衍生出來的)
於是從第1件物品開始,一件一件決定放還是不放。
我們可以寫下偽**
for i=1:n
for j=0:v;}
else
}當然我們得初始化f[0][0]-----f[0][v]都為0(以上偽**省略)
滾動陣列:
注意到我們的動態轉移方程f[i][j] = max;
當前狀態只與二維陣列的前一行有關,那麼之前的那些行都沒用了啊,何必在那裡浪費空間呢?這就引申了滾動陣列的乙個概念(其實就是一維陣列啦)為何叫滾動呢?因為是一行一行,要求的當前行只與上一行有關。到此我們的0-1揹包就可以用一維陣列來求解了。看完下面,我們就可以徹底理解滾動陣列了。
我們定義f[v]為前i件物品放進容量為v的揹包裡所獲得的最大價值
於是寫下動態轉移方程: f[v]=max
那麼這次寫程式還是j從0開始迴圈麼?(也就是上表從左到右一行一行遍歷)顯然不對,因為這樣我們就沒有利用到前一行所求的結果。只有j從v開始才能保證是從利用到前一行的值(仔細想想這裡),因為我們在更新f[v]之前f[v-c[i]]的值還在嘛。再想想滾動陣列,是不是有點體會了?滾動滾動,從右到左,既更新了當前的值,也利用到了上一行的值。大大節省了記憶體空間。
現在我們可以寫下程式了
for i=1..n
for j=v..0
f[j]=max;
二、完全揹包
定義:
何謂完全揹包呢?其實和0-1揹包一樣,只是現在每一樣物品都有無限件可以選。只是乙個量的不同。
與0-1揹包的聯絡:
那麼他又和0-1揹包有什麼聯絡呢?在此引用一下dd大牛講的。我們可以這樣想:第i件物品有v/c[i]件(因為揹包最多放v/c[i]件編號為i的物品嘛)然後把他們都看成是體積相同,價值相同的不同物品,這不就成了0-1揹包問題嗎?當然這裡只是講一下二者的聯絡,如果這麼去做。顯然耗時是很多的。
求解:
稍微想一下我們就可以寫下動態轉移方程f[i+1][j] = max 0<=k<=j/c[i]
用白話講就是第i件物品選k件放入揹包所能獲得的最大價值
顯然我們可以寫乙個三重迴圈的程式出來,不過效率很低。稍微分析一下。
在f[i+1][j]的計算中選擇k個的情況與在f[i+1][j-c[i]]的計算中選擇k-1個的情況是一樣的,所以f[i+1][j]的遞推中k>=1的部分的計算已經在f[i+1][j-c[i]]的計算中完成了。那麼可以根據如下方式進行變換。
f[i+1][j] = max
= max k>=1}
= max k>=0} (注意聯絡k的取值範圍)
= max
於是可以寫出下面的兩重迴圈的程式
void solve()
初學者可能會感到詫異,為什麼這裡沒看到取k個中的最大價值呢?
其實內層迴圈就隱含了這個意思了啊:先放第一件物品,內層迴圈就是試探當前放幾個第i件物品,容量為j時取最大價值啊。
比如第一件體積是2 價值是3 揹包總容量是7 一次迴圈之後f[1]=0,f[2]=3,f[3]=3,f[4]=6 f[5]=6 f[6]=9 f[7]=9
那麼為什麼這裡j是從0...v呢?
先來對比一下0-1揹包和完全揹包的動態轉移方程
0-1揹包: f[j]=max
完全揹包: f[j]=max |0<=k*c[i]<=v
對於0-1揹包,前面已經講了要求f[v] ,則必須f[v-c[i]]的值還沒更新,因為他是在兩者之中取大值嘛。所以j從v倒退著走。
而對於完全揹包,我們注意他的動態轉移方程。要更新f[j]則必然先更新了f[j-k*c[i]],換句話說,一旦第i件物品加入之後,則勢必影響之後的最優解,還是拿上面那個例子來說
第一件體積是2 價值是3 揹包總容量是7 一次迴圈之後f[1]=0,f[2]=3,f[3]=3,f[4]=6 f[5]=6 f[6]=9 f[7]=9
加入第二件物品(體積是3,價值是5)之後 f[1]=0,f[2]=3,f[3]=5...從此之後f[3]就已經影響後面的解了。可以動手寫寫。
完全揹包到此結束。。
三:多重揹包
定義:其實也是和0-1揹包差不多,只不過是對於第i件物品,有有限個個數為m[i],這三種揹包問題其實就只有乙個區別嘛,那就是某件物品的量的差別。
解法:有了前面的基礎,寫下這個動態轉移方程還不是輕而易舉麼?
優化的方法:
運用神奇的二進位制,進行物品拆分,轉化成01揹包
物品拆分,把13個相同的物品分成4組(1,2,4,6)
用這4組可以組成任意乙個1~13之間的數!
原理:乙個數總可以用2^k表示
而且總和等於13,所以不會組成超過13的數
所以可將一種有c個的物品拆分成1,2,4,...,2^(k-1),c-(2^k-1)
然後進行01揹包
揹包問題 01揹包總結
寫這篇部落格的原因是因為自己初學揹包的時候覺得好玄學。只是知道怎麼寫,但是具體是為什麼覺得很玄妙。在此其實希望和我一樣的小白萌新早點明白其中的原理,其實原理很簡單,只要懂了這個圖,我想01揹包就不成問題了。首先要明確這張表是至底向上,從左到右生成的。關於01揹包的題目暫時整理了一點。1.簡單01揹包...
揹包問題總結
標籤 acm dp 揹包 n 物品,乙個揹包,每個物品價值wi 體積vi 揹包容量 c 求最大價值 對於物品 i可選可不選 fi j fi 1 j vi j 0 fi j max c j vi 給定 n 種物品和乙個揹包。第 i種物品的價值是 wi 其體積為vi 揹包的容量為 c 同一種物品的數量無...
揹包問題總結
n件物品,v容量,每件物品的代價v i 價值w i 01揹包 每種物品只能取乙個,for i 1 to n for j v to v i f j f j v i w i 完全揹包 每種物品可以取無限個,但是體積不能超過v,for i 1 to n for j v i to v f j f j v i...