1. 程式人生 > >0-1背包問題的動態規劃法與回溯法

0-1背包問題的動態規劃法與回溯法

-- cstring namespace 動態 com 最優 end 背包 分享圖片

一、動態規劃

狀態轉移方程:

 1 從前往後:
 2 if(j>=w[i])
 3     m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
 4 else
 5     m[i][j]=m[i-1][j];
 6  
 7 從後往前:
 8 if(j>=w[i])
 9     m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
10 else
11     m[i][j]=m[i+1][j];

算法:

 1 從前往後:
 2 for(int i=1;i<=n;i++)
 3     for(int
j=1;j<=c;j++) 4 { 5 if(j>=w[i]) 6 { 7 m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]); 8 } 9 else//這裏沒有考慮j<0的情況,因為算法中j取不到 10 { 11 m[i][j]=m[i-1][j]; 12 } 13 } 14 15 從後往前: 16 for(int i=n;i>=1;i--) 17 for(int
j=1;j<=c;j++) 18 { 19 if(j>=w[i]) 20 { 21 m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]); 22 } 23 else 24 { 25 m[i][j]=m[i+1][j]; 26 } 27 }

例子:

例:0-1背包問題。在使用動態規劃算法求解0-1背包問題時,使用二維數組m[i][j]存儲背包剩余容量為j,可選物品為i、i+1、……、n時0-1背包問題的最優值。繪制

重量數組w = {4, 6, 2, 2, 5, 1},

價值數組v = {8, 10, 6, 3, 7, 2},

背包容量C = 12時對應的m[i][j]數組。(從前往後)

技術分享圖片

例題代碼 :

 1 #include<iostream>
 2 #include<cmath>
 3 #include<cstring>
 4 #define N 20
 5 using namespace std;
 6 int main()
 7 {
 8     int w[N]={0,4,6,2,2,5,1},v[N]={0,8,10,6,3,7,2};
 9     int m[N][N];
10     memset(m,0,sizeof(m));
11     int n=6,c=12;   //n,c均要小於N 
12     for(int i=1;i<=n;i++)
13     for(int j=1;j<=c;j++)
14     {
15         if(j>=w[i])
16         {
17             m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);
18         }
19         else
20         {
21             m[i][j]=m[i-1][j];
22         }
23     }
24     cout<<m[n][c]<<endl; //從前往後
25  
26     /*
27     for(int i=n;i>=1;i--)
28     for(int j=1;j<=c;j++)
29     {
30         if(j>=w[i])
31         {
32             m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
33         }
34         else
35         {
36             m[i][j]=m[i+1][j];
37         }
38     }
39     cout<<m[1][c]<<endl;//從後往前
40     */
41     return 0;
42 }

二、回溯法

1進入左子樹條件:cw+w[i]<=c //cw為當前重量

2進入右子樹條件(減枝函數):cp+r>bestp //cp為當前價值,bestp為當前最優價值,r為當前剩余物品價值總和。cp+r由函數 Bound計算。

3需要先將物品按單位重量價值從大到小排序,按序進入左子樹;進入右子樹時,由函數Bound計算當前節點上界,只有其上界大於當前最優價值bestp時,才進入右子樹,否則減去。

算法:

 1 void Backtrack(int i)
 2 {
 3     if(i>n)  //到達葉節點
 4     {
 5         bestp=cp; 
 6         return;
 7     }
 8     if(cw+w[i]<=c)  //進入左子樹
 9     {
10         cw+=w[i];
11         cp+=v[i];
12         Backtrack(i+1);
13         cw-=w[i];
14         cp-=v[i];
15     }
16     if(Bound(i+1)>bestp)  //進入右子樹
17     {
18         Backtrack(i+1);
19     }
20 } 
21  
22 int Bound(int i)  //計算上界
23 {
24     int cleft=c-cw;
25     int b=cp;          
26     while(i<=n&&w[i]<=cleft)  //以物品單位重量價值遞減序裝入物品
27     {
28         cleft-=w[i];
29         b+=v[i];
30         i++;
31     }
32     if(i<=n)//裝滿背包
33     {
34         b+=v[i]*(cleft/w[i]);
35     }
36     return b;
37 }

例子代碼:

 1 #include<iostream>
 2 #define N 20
 3 using namespace std;
 4 int w[N]={0,4,6,2,2,5,1},v[N]={0,8,10,6,3,7,2};
 5 int n=6,c=12;
 6 int cp=0,cw=0,bestp=0;
 7 int Bound(int i)  //計算上界
 8 {
 9     int cleft=c-cw;
10     int b=cp;          
11     while(i<=n&&w[i]<=cleft)  //以物品單位重量價值遞減序裝入物品
12     {
13         cleft-=w[i];
14         b+=v[i];
15         i++;
16     }
17     if(i<=n)//裝滿背包
18     {
19         b+=v[i]*(cleft/w[i]);
20     }
21     return b;
22 }
23 void Backtrack(int i)
24 {
25     if(i>n)  //到達葉節點 
26     {
27         bestp=cp; 
28         return;
29     }
30     if(cw+w[i]<=c)  //進入左子樹
31     {
32         cw+=w[i];
33         cp+=v[i];
34         Backtrack(i+1);
35         cw-=w[i];
36         cp-=v[i];
37     }
38     if(Bound(i+1)>bestp)  //進入右子樹 
39     {
40         Backtrack(i+1);
41     }
42 } 
43  
44 int main()
45 {
46     Backtrack(1);
47     cout<<bestp<<endl;
48     return 0;
49 }

0-1背包問題的動態規劃法與回溯法