0-1背包問題的動態規劃法與回溯法
阿新 • • 發佈:2018-12-28
-- 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(intj=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(intj=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背包問題的動態規劃法與回溯法