動態規划之01背包詳解【解題報告】


01背包問題,是用來介紹動態規划算法最經典的例子,網上關於01背包問題的講解也很多,我寫這篇文章力爭做到用最簡單的方式,最少的公式把01背包問題講解透徹。

01背包的狀態轉換方程 f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ),  f[i-1,j] }

f[i,j]表示在前i件物品中選擇若干件放在承重為 j 的背包中,可以取得的最大價值。
Pi表示第i件物品的價值。
決策:為了背包中物品總價值最大化,第 i件物品應該放入背包中嗎 ?
 

題目描述:

有編號分別為a,b,c,d,e的五件物品,它們的重量分別是2,2,6,5,4,它們的價值分別是6,3,5,4,6,現在給你個承重為10的背包,如何讓背包里裝入的物品具有最大的價值總和?

 

name weight value 1 2 3 4 5 6 7 8 9 10
a 2 6 0 6 6 9 9 12 12 15 15 15
b 2 3 0 3 3 6 6 9 9 9 10 11
c 6 5 0 0 0 6 6 6 6 6 10 11
d 5 4 0 0 0 6 6 6 6 6 10 10
e 4 6 0 0 0 6 6 6 6 6 6 6

 

只要你能通過找規律手工填寫出上面這張表就算理解了01背包的動態規划算法。

首先要明確這張表是至底向上,從左到右生成的。

為了敘述方便,用e2單元格表示e行2列的單元格,這個單元格的意義是用來表示只有物品e時,有個承重為2的背包,那么這個背包的最大價值是0,因為e物品的重量是4,背包裝不了。

對於d2單元格,表示只有物品e,d時,承重為2的背包,所能裝入的最大價值,仍然是0,因為物品e,d都不是這個背包能裝的。

同理,c2=0,b2=3,a2=6。

對於承重為8的背包,a8=15,是怎么得出的呢?

根據01背包的狀態轉換方程,需要考察兩個值,

一個是f[i-1,j],對於這個例子來說就是b8的值9,另一個是f[i-1,j-Wi]+Pi;

在這里,

 f[i-1,j]表示我有一個承重為8的背包,當只有物品b,c,d,e四件可選時,這個背包能裝入的最大價值

f[i-1,j-Wi]表示我有一個承重為6的背包(等於當前背包承重減去物品a的重量),當只有物品b,c,d,e四件可選時,這個背包能裝入的最大價值

f[i-1,j-Wi]就是指單元格b6,值為9,Pi指的是a物品的價值,即6

由於f[i-1,j-Wi]+Pi = 9 + 6 = 15 大於f[i-1,j] = 9,所以物品a應該放入承重為8的背包

 1 #include<iostream>
 2 using namespace std;
 3 #define  V 1500
 4 unsigned int f[10][V];//全局變量,自動初始化為0
 5 unsigned int weight[10];
 6 unsigned int value[10];
 7 #define  max(x,y)    (x)>(y)?(x):(y)
 8 int main()
 9 {
10     
11     int N,M;
12     cin>>N;//物品個數
13     cin>>M;//背包容量
14     for (int i=1;i<=N; i++)
15     {
16         cin>>weight[i]>>value[i];
17     }
18     for (int i=1; i<=N; i++)
19         for (int j=1; j<=M; j++)
20         {
21             if (weight[i]<=j)
22             {
23                 f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);
24             }
25             else
26                 f[i][j]=f[i-1][j];
27         }
28     
29     cout<<f[N][M]<<endl;//輸出最優解
30 }

可以進一步優化內存使用。上面計算f[i][j]可以看出,在計算f[i][j]時只使用了f[i-1][0……j],沒有使用其他子問題,因此在存儲子問題的解時,只存儲f[i-1]子問題的解即可。這樣可以用兩個一維數組解決,一個存儲子問題,一個存儲正在解決的子問題。

 

再進一步思考,計算f[i][j]時只使用了f[i-1][0……j],沒有使用f[i-1][j+1]這樣的話,我們先計算j的循環時,讓j=M……1,只使用一個一維數組即可。

for i=1……N

for j=M……1

f[j]=max(f[j],f[j-weight[i]+value[i])

 

 1 #include<iostream>
 2 using namespace std;
 3 #define  V 1500
 4 unsigned int f[V];//全局變量,自動初始化為0
 5 unsigned int weight[10];
 6 unsigned int value[10];
 7 #define  max(x,y)    (x)>(y)?(x):(y)
 8 int main()
 9 {
10     
11     int N,M;
12     cin>>N;//物品個數
13     cin>>M;//背包容量
14     for (int i=1;i<=N; i++)
15     {
16         cin>>weight[i]>>value[i];
17     }
18     for (int i=1; i<=N; i++)
19         for (int j=M; j>=1; j--)
20         {
21             if (weight[i]<=j)
22             {
23                 f[j]=max(f[j],f[j-weight[i]]+value[i]);
24             }            
25         }
26     
27     cout<<f[M]<<endl;//輸出最優解
28 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM