揹包問題全記載

2021-09-25 22:29:47 字數 2899 閱讀 8585

說明:部分揹包相對簡單不再贅述,後三者著重記錄一下0-1揹包問題,因為大同小異都是通用,核心思想是一樣的,尤其是利用一維陣列解決問題的辦法一定要記好。

一:部分揹包問題

問題分析:即可以把物品分割開裝入,相對來說是最容易理解的

方法:貪心演算法,優先放入''價量比''(價值除以質量)最大的,直到揹包不能再放入(或物品已取完)。

二:0-1揹包問題

問題分析:面對多種類別的物品,我們的選擇只有裝或者不裝,不能多次裝更不能分割後裝

給定 n 種物品和乙個容量為 c 的揹包,物品 i 的重量是 wi,其價值為 vi

設乙個k[n][c] 的二維陣列,k[ i ][ j ] 表示 拿上第i個物品的時候,揹包容量為  j 時所能獲得的最大價值 

方法:動態規劃,在當前揹包容量的基礎下可以獲得的最大收益(拿還是不拿,拿了到底夠不夠大,不夠大相當於還是不拿)

具體演算法分析:

若 j < w[i] 的情況,這時候揹包放不下第 i 件物品:k[ i ][ j ] = k[ i-1 ][ j ](這個點判斷結束,繼續向後搜尋)

若 j>=w[i] 的情況,這時揹包可以放下第 i 件物品,則繼續考慮是否能獲取更大的價值。

如果確定留下,k[ i ][ j ]=k[ i-1 ][ j-w[ i ] ] + v[ i ](加上拿進來的物品的價值,同時要把消耗的容量減去)

如果不拿,k[ i ][ j ] = k[ i-1 ][ j ] 

故重點在於狀態轉移方程(動態規劃專業名詞,我也不喜歡這麼叫,或者叫推導式哇):

if(j>=w[i])               //揹包可以放下

k[i][j]=max(k[i-1][j],k[i-1][j-w[i]]+v[i]);//動態規劃的核心,在當前問題下判斷,最後總和

else //揹包放不下

k[i][j]=k[i-1][j];

靠這個狀態轉移方程為核心,可以解決很多麻煩,當然這僅僅解決了最優解值的問題(若問題只問最後裝的結果,那麼就可以放心去ac了),但如果問道關於最優解(說白了就是到底是拿了哪幾個東西呢?)的時候,我們就需要再斟酌一下了。

那麼我們需要再多乙個函式來追逐具體物品,x[i]=0表示不拿,x[i]=1表示拿。如果k[n][c]=k[n-1][c] ,說明沒拿,則x[n]=0 ; 否則 x[n]=1。當x[n]=0時,由x[n-1][c]繼續構造最優解;當x[n]=1時,則由x[n-1][c-w[i]]繼續構造最優解,以此類推。(注意這個位置很巧妙的地方在於是倒著來的,在得到最優解值的條件下完成)

void traceback()

}x[1]=(k[1][c]>0)?1:0;

}

注:二維的方式便於理解,但是會有侷限性(時間複雜度等),故有必要談到利用一維的方式(而且在解決其他型別的揹包問題的時候也會用到):

設k[j]表示重量不超過j公斤的最大價值 可得出狀態轉移方程 :kj=maxj(a[i]中表示重量,b[i]中表示價值)

//a[i]中表示重量,b[i]中表示價值

#includeusing namespace std;

int main()

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

cout<三:完全揹包問題

問題分析:完全揹包表示每個物品可以取無限次,是0-1揹包問題的衍生,唯一限制只要加起來總容量不超過c就可以。

方法:動態規劃,核心同樣在於狀態轉移方程

具體演算法分析:其實和01揹包問題一樣,只是初始化的值和遞推公式需要稍微變化一下。不要被無限個所嚇倒,依舊拿著動態規劃的那乙個標尺來尋找:截至到目前的最優解!(即考慮揹包容量有多少,而且我可以選擇的物品的範圍是什麼)                   

同樣可以用kj表示前i間物品恰放入乙個容器為j的揹包可以獲得的最大價值:

則其狀態轉移方程依舊為:kj=maxj

同樣貼上**(注意和0-1揹包的區別):

#includeusing namespace std;

int main()

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

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

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

cout<四:多重揹包問題

問題分析:多重揹包中每個物品的個數都是給定的,不是乙個,也不是無限個。

方法:動態規劃,核心同樣在於狀態轉移方程

具體演算法分析:它與完全揹包有共同點是每個物品都有了一定的數量,狀態轉移方程更新為: k[j]=max

直接貼**看區別:

#includeusing namespace std;

int main()

int f[10001];

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

for(int j=m;j>=0;j--)

for(int k=0;k<=c[i];k++)

cout<總結:以上寫的基於在學校學的淺層次的演算法基礎上,根據各位csdn大佬部落格總結歸納出來的(很多**很簡潔明瞭,我就直接用了,可以詳細參考文章開頭的鏈結)。揹包問題的核心就在於那個推導式,要有充分理解後運用在實戰中,不要看完理解了事。

揹包問題全解

型別一 可分割性的物品 此型別為貪心問題 時間限制 3000 ms 記憶體限制 65535 kb 難度 3 描述 現在有很多物品 它們是可以分割的 我們知道它們每個物品的單位重量的價值v和重量w 1 v,w 10 如果給你乙個揹包它能容納的重量為m 10 m 20 你所要做的就是把物品裝到揹包裡,使...

0 1揹包問題全解析

0 1揹包問題 給定n個重量為w1 w2 w3.wn,價值為v1 v2 v3.vn的物品,容量為c的揹包,求這個揹包可以裝下的價值最高的子集,每個物品只能使用一次 w 重量v 價值 c 5 容量 最佳子集為 2,1,2 12 10 15 37 對每個物品,都有選擇 不選兩個狀態,這樣解空間就可以描述...

揹包問題 01揹包問題

n個物品,總體積是v,每個物品的體積的vi,每個物品的最大價值是wi,在不超過v的體積下求最大價值 eg揹包容積為 5 物品數量為 4 物品的體積分別為 物品的價值分別為 思路定義乙個二位陣列int f new int n 1 v 1 f i j 就表示在1 i個物品中選取體積小於v的情況的最大價值...