c 揹包問題

2022-02-16 15:49:44 字數 4085 閱讀 6850

又鴿了好久……

博主剛剛學會揹包問題不久,然後有一段時間沒練習了

今天就來重新溫習一下,順手就寫了這一篇部落格。

好了,下面進入正題!

揹包問題是動態規劃的乙個分支

主要是分成了01揹包、完全揹包和多重揹包。

下面從01揹包開始講解。

01揹包是在m件物品取出若干件放在空間為w的揹包裡,每件物品的體積為w1,w2至wn,與之相對應的價值為p1,p2至pn。01揹包可謂是揹包問題中最簡單的問題。01揹包的約束條件是給定幾種物品,每種物品有且只有乙個,並且有權值和體積兩個屬性。在01揹包問題中,因為每種物品只有乙個,對於每個物品只需要考慮選與不選兩種情況。如果不選擇將其放入揹包中,則不需要處理。如果選擇將其放入揹包中,由於不清楚之前放入的物品佔據了多大的空間,需要列舉將這個物品放入揹包後可能佔據揹包空間的所有情況。

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

了解了基本概念和問題雛形後我們就可以來想做題的方法了。

從題目裡看,我們就能看出,01揹包有個特點:每種物品僅有一件,可以選擇放或不放。

所以我們就可以把每種情況都列舉一遍。

首先建立乙個二維陣列dp表示價值,w[i]是每件物品的價值,c[i]是每件物品的體積

然後就想,由於它只有放和不放兩種狀態,我們就要比較這兩種狀態的價值,用max函式。

狀態轉移方程:dp[i][v]=max

其中,dp[i-1][v]表示不放該物品,dp[i-1][v-c[i]]+w[i]表示放入該物品。

這樣做乙個迴圈列舉各種情況即可。

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

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

在這裡,揹包放入物品後,容量不斷的減少,直到再也放不進了

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

最後的結果就是最大值。

還有一些優化的方法:01滾動就地滾動

01滾動:

我們可以看到每一行的結果實際上只與上一行有關,所以就可以01滾動——f[i][0,1] 一行記錄前一行的值,另一行記錄當前行的值……

所以,這是一種簡化的好辦法!

就地滾動:

就地滾動就是用乙個一維陣列,把之前的狀態和當前的狀態放在同乙個陣列,但是在寫的過程中會有問題。

先上**吧:

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

for(j= c[i]; j)     

if(!dp[j-c[i]) dp[j] = dp[j-c[i]];

我們會發現,這樣的話乙個物品會被重複計算多次。

問題1:採藥

這是乙個經典的問題哦!

飛機場:洛谷p1048 採藥

問題解答(不可用於直接ac本題,可進行參考!)

1 #include2 #include3 #include4 #include5 #include6

#define inf 1000000007//

狀態:dp[i][j]表示考慮前i個草藥,目前體積之和為j,可以獲得的最大價值8//

轉移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]) 9//

答案:dp[m][max(...)]

10//

不需要初始化,直接算

11//

複雜度:o(m*t)

12int dp[105][1005],w[105],v[105

];13

using

namespace

std;

14int

main()

1522

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

23for(int j=t;j>=0;j--)

2430

int ans=0;31

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

3235 cout<

36return0;

37 }

當然還有一種做法。可以直接使用一維陣列:參見一本通。

這個問題非常類似於01揹包問題,所不同的是每種物品有無限件,也就是從每種物品的角度考慮,與它相關的策略已並非取或不取兩種,而是有取0件、取1件、取2件……取[v/c]件等很多種。

有 n 種物品和乙個容量為 v 的揹包,每種物品都有無限件可用。放入第 i 種物品的費用是 ci ,價值是 wi 。求解:將哪些物品裝入揹包,可使這些物品的耗費的費用總和不超過揹包容量,且價值總和最大。

這個題目有乙個和01揹包不一樣的地方:每種物品有無數件!

然後我們想前面我說過的就地滾動,會計算多次,這不正巧?

問題2:瘋狂的採藥

這個……這個題目的介紹不大正經,未成年人請在家長的陪伴下**。

飛機場:洛谷p1616 瘋狂的採藥

問題解答(不可用於直接ac本題,可進行參考!)

1 #include2 #include3 #include4 #include5 #include6

#define inf 1000000007//

狀態:dp[i][j]表示考慮前i個草藥,目前體積之和為j,可以獲得的最大價值8//

轉移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]) 9//

答案:dp[m][max(...)]

10//

不需要初始化,直接算

11//

複雜度:o(m*t)

12int f[1000001

],t,m,ti,v;

13using

namespace

std;

14int

main()

1523 cout<

24return0;

25 }

有n種物品和乙個容量為v的揹包。第i種物品最多有n[i]件可用,每件體積是w[i],價值是v[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。

這個問題的特點是:每種物品有一定數量

這個運用的是二進位制思想

轉化為01揹包求解:把第i種物品換成n[i]件01揹包中的物品,則得到了物品數為σn[i]的01揹包問題。

我們考慮把第i種物品換成若干件物品,使得原問題中第i種物品可取的每種策略——取0..n[i]件——均能等價於取若干件代換以後的物品。

另外,取超過n[i]件的策略必不能出現。 方法是:將第i種物品分成若干件物品,其中每件物品有乙個係數,這件物品的費用和價值均是原來的費用和價值乘以這個係數。

使這些係數分別為1,2,4,...,2^(k-1),n[i]-2^k+1,且k是滿足n[i]-2^k+1>0的最大整數。例如,如果n[i]為13,就將這種物品分成係數分別為1,2,4,6的四件物品。

解決問題的道理

1) 1+2+4+...+2^(k-1)+n[i]-2^k+1 = n[i]   這就保證了最多為n[i]個物品

2)1,2,4,……,2^(k-1),可以湊出1到2^k – 1的所有整數(聯絡乙個數的二進位制拆分即可證明,證明過程在下面的題解中)

3) 2^k……n[i]的所有整數可以用若干個上述元素湊出(可以理解為湊n[i]-t, 而n[i]為上面所有數的和,t則是乙個小於2^k 的數,那麼在所有的數中去掉組成2^k 的那些數剩下的就可以組成n[i]-t了)

當然,這個二進位制的道理我在之前的一篇部落格上寫過,一會的實戰演練會帶你們去。

問題3:寶物篩選

這是一道很水的藍題……

飛機場:洛谷p1776 寶物篩選題解(我寫的,你們還有更詳細的多重揹包解決思路)

客官,給個贊再走唄?

C 揹包問題

在0 1揹包中,物體或者被裝入揹包,或者不被裝入揹包,只有兩種選擇 迴圈變數i,j的意義 前i個物品能夠裝入載重量為j的揹包中 陣列value的意義 value i j 表示前i個物品能加載重量為j的揹包中的最大價值 若w i j,第i個物品不被裝入揹包,否則,第i個物品放入揹包後的最大價值 val...

C 揹包問題

有許多東西要放入揹包,但揹包容量有限,現給出物品的種類,以及每種物品的數量 體積和價值,求出揹包能夠裝物品的最大價值。輸入有多組資料,每組資料的第一行有2個整數,物品種數n 1 n 2000 和揹包裝載體積v 1 v 500 2行到i 1行每行3個整數,為第i種物品的數量m 1 m 5000 體積w...

c 揹包問題

動態規劃和貪心中的五個揹包問題,收集並整理出來放在這裡。0 1揹包問題是一道動規的題,其題目意思就是每樣物品只能取乙個。二維 中概括來說是上面位置的數 上面的位置減物品質量這個數值後這個位子上的數加物品的價值比較哪個大,這裡為了方便都轉換為一維陣列來做。一樣的 include using names...