01揹包問題

2021-08-10 15:57:19 字數 4244 閱讀 3055

情景預設:

01揹包是在i件物品取出若干件放在空間為j的揹包裡,第i件物品的重量為t[i],與之相對應的價值為v[i]。

01揹包是揹包問題中最簡單的問題。01揹包的約束條件是給定幾種物品,每種物品有且只有乙個,並且有權值和重量兩個屬性。

在01揹包問題中,因為每種物品只有乙個,對於每個物品只需要考慮選與不選兩種情況。如果不選擇將其放入揹包中,則不需要處理。如果選擇將其放入揹包中,由於不清楚之前放入的物品佔據了多大的重量,需要列舉將這個物品放入揹包後可能佔據揹包空間的所有情況。

這個情境下的題目要求是:選取哪些物品可以在不超過揹包最大承載質量下滿足最大的價值要求。

首先我們將這個總共的最大價值要求理解一下,這個總最大價值取決於之前每一步的最大價值,每一次物品是否放入的選擇就是要判斷,當它放入或者不放的時候前面的包內的最大價值,如果放入的話,就要考慮總容量減去這個物品質量之後的最大價值與現在這個物品的最大價值之和,還要考慮這件物品不放入時包內容量的最大價值,將這兩種情況作比較,取最大價值。

給出以下方程:

dp[i][j] = max ;

再來解釋一下這個方程:

① dp[i][j]指的是前i件物品放入容量為j的揹包中時最大價值;

② dp[i-1][j-t[i]]指的是當將第i件物品放入時揹包容量變成了j-t[i]時前i-1件物品所取得的最大價值,那麼dp[i-1][j-t[i]] + v[i]指的是第i件物品放入時的前i件物品放入容量為j的揹包時的最大價值;

③ dp[i-1[j]指的是當第i件物品不放入揹包時揹包容量依舊是j,考慮前i-1件物品放入揹包中的最大價值;

比較這兩種情況得到其中獲得的最大價值,那麼這就是前i件物品放入容量為j的揹包時的最大價值。

最後一次的最大價值,要去考慮之前的最大價值,那麼對於每次這樣的考慮就可以認為是我們需要知道每次dp[i-1][j-t[i]]的最大價值,即在每次對於物品的存放時都要更新除去當前物品質量的揹包容量下的最大價值,用[j-t[i]]去找已經存入的最大價值,再與v[i]相加後和dp[i-1][j]作比較。所以有必要建立乙個長度大於揹包容量的陣列,下標表示的是當前的容量,這個單位表示的就是在這容量下的最大價值,每次迴圈都會更新當前容量的最大價值。

現在給出乙個栗子:(這裡用一維陣列來解)

讓我假設現在的揹包的容量是c=10;

物品編號: 1 2 3

物品重量: 5 6 4

物品價值:20 10 12

分析過程:

dp:0 0 0 0 0 0 0 0 0 0

i=1:

dp[10] = max(dp[5]+20, dp[10]);

dp[9] = max(dp[4]+20, dp[9]);

dp[8] = max(dp[3]+20, dp[8]);

dp[7] = max(dp[2]+20, dp[7]);

dp[6] = max(dp[1]+20, dp[6]);

dp[5] = max(dp[0]+20, dp[5]);

當j小於5的時候是放不下的,所以容量從1到4的價值全都為0。

dp: 0 0 0 0 20 20 20 20 20 20

i=2:

dp[10] = max(dp[4]+10, dp[10]);

dp[9] = max(dp[3]+10, dp[9]);

dp[8] = max(dp[2]+10, dp[8]);

dp[7] = max(dp[1]+10, dp[7]);

dp[6] = max(dp[0]+10, dp[6]);

當j小於6的時候是放不下的,所以容量從1到6的價值沒有辦法更新。

dp: 0 0 0 0 20 20 20 20 20 20

i=3:

dp[10] = max(dp[6]+12, dp[10]);

dp[9] = max(dp[5]+12, dp[9]);

dp[8] = max(dp[4]+12, dp[8]);

dp[7] = max(dp[3]+12, dp[7]);

dp[6] = max(dp[2]+12, dp[6]);

dp[5] = max(dp[1]+12, dp[5]);

dp[4] = max(dp[0]+12, dp[4]);

這一次更新了一部分的值,凡是大於4的都是可以存放的。

dp: 0 0 0 12 20 20 20 20 32 32

dp[10]就是揹包容量為10的時候的最大價值,就是要求的值了,可以換種理解方式:你揹包越大裝的東西越多,為什麼要選小的呢???可以看到,容量大的時候的值取決於容量小的時候的值,從而不斷被正確更新,每次大的容量都需要去找小容量的最大價值來更新當前值,因此對於小容量的記錄和更新也是有必要的。

你問我為什麼要倒著往裡放???這就給出一種正著放進去的情景。

讓我假設現在的揹包的容量是c=10;

物品編號: 1 2 3

物品重量: 5 6 4

物品價值:20 10 12

分析過程:

dp:0 0 0 0 0 0 0 0 0 0

i=1:

dp[5] = max(dp[0]+20, dp[5]);

dp[6] = max(dp[1]+20, dp[6]);

dp[7] = max(dp[2]+20, dp[7]);

dp[8] = max(dp[3]+20, dp[8]);

dp[9] = max(dp[4]+20, dp[9]);

dp[10] = max(dp[5]+20, dp[10]);

小於5的放不進去,5到10的全都放進去了。

dp[5]=20,dp[6]=20……dp[10] = dp[5] + 20 =40

注意這裡的dp[10]變成了40,原來不是20的麼??因為dp[5]已經被放進去了一次了,dp[10]的時候又放進去了一次,已經不符合只放如一次的規則了!

因此01揹包問題千萬不要正著放入,正著放入的是物品有無限件,那是另乙個問題:完全揹包。

給出一道例題:

sdnu-oj-1033 採藥

description

辰辰是個天資聰穎的孩子,他的夢想是成為世界上最偉大的醫師。為此,他想拜附近最有威望的醫師為師。醫師為了判斷他的資質,給他出了乙個難題。醫師把他帶到乙個到處都是草藥的山洞裡對他說:「孩子,這個山洞裡有一些不同的草藥,採每一株都需要一些時間,每一株也有它自身的價值。我會給你一段時間,在這段時間裡,你可以採到一些草藥。如果你是乙個聰明的孩子,你應該可以讓採到的草藥的總價值最大。」

如果你是辰辰,你能完成這個任務嗎?

input

輸入的第一行有兩個整數t(1 <= t <= 1000)和m(1 <= m <= 100),用乙個空格隔開,t代表總共能夠用來採藥的時間,m代表山洞裡的草藥的數目。接下來的m行每行包括兩個在1到100之間(包括1和100)的整數,分別表示採摘某株草藥的時間(1 <= t <= t)和這株草藥的價值(1 <= v <= 100000)。

output

輸出包括一行,這一行只包含乙個整數,表示在規定的時間內,可以採到的草藥的最大總價值。

sample input

100 5

77 92

22 22

29 87

50 46

99 90

sample output

133這裡的採藥時間就是揹包容量,價值就是對應的價值。

利用兩個陣列儲存容量和價值,每次考慮存放的關鍵偽**如下:

for(int i = 0; i < n; i++)      //考慮每株草藥

; //取兩種情況較大的}}

}

以下給出完整**:

#include 

#include

#include

using

namespace

std;

int main()

; for(int i = 0; i < m; i++)

for(int i = 0; i < m; i++)

}cout

<< dp[time] << endl;

}return

0;}

揹包問題 01揹包問題

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

揹包問題 01揹包

有n件物品和乙個容量為v的揹包。第i件物品的重量是c i 價值是w i 求解將哪些物品裝入揹包可使價值總和最大。01揹包中的 01 就是一種物品只有1件,你可以選擇放進去揹包即1,也可以選擇不放入揹包中即0。include include using namespace std const int ...

揹包問題(01揹包)

1085 揹包問題 在n件物品取出若干件放在容量為w的揹包裡,每件物品的體積為w1,w2 wn wi為整數 與之相對應的價值為p1,p2 pn pi為整數 求揹包能夠容納的最大價值。input 第1行,2個整數,n和w中間用空格隔開。n為物品的數量,w為揹包的容量。1 n 100,1 w 10000...