动态规划之0-1背包问题
问题描述:给定n种物品,1个背包,背包容量为c,每个物品i的价值为vi,重量为wi,如何选择装入物品能使背包的总价值最大?
注意:1)对于每个物品来说,只有两种选择,要么装,要么不装!
2)不能将物品i装入背包多次,也不能只装入部分物品!
eg.c = 100 3种物品
1:v1 = 50 w1 = 30
2:v2 = 60 w2 = 20
3:v3 = 20 w3 = 70
形式化描述:给定c >0, wi >0, vi >0 , 1≤i≤n.要求找一n元向量A=(x1,x2,…,xn), xi∈{0,1}(0表示“不装”,1表示“装”),1<=i<=n,使得 ∑ wixi≤c【物品的重量和小于背包总容量】而且∑ vixi达到最大。
递归关系:
对于物品有两个选择,要么装,要么不装,对于n个物品,则要做n种选择。现在设m[i][j]是背包容量为j,可选物品为i,i+1,...n【1<=i<=n】的0-1背包问题的最优解,找出递推公式:
<1>:如果放入第i件物品,那么m[i][j]=m[i+1][j-wi]+vi(可选物品变为i+1,i+2,....n,容量减去i的重量,价值加上i的价值)
<2>:如果不放入第i件物品,那么m[i][j]=m[i+1][j](可选物品变为i+1,i+2,....n,容量不变,价值不变)
设置循环关系时,采用自底向上的动态规划,先考虑当可选物品为最后一件物品(i=n,0<=j<=c)时,m[n][j]的取值(根据j的取值判断能否放下得出m[n][j]的取值),接下来i应该从n-1开始变化到1,表示可选物品物品开始变成i,i+1,...n(同样根据j的取值判断能否放得下得到m[n][j]的取值)
要么放要么不放,所以取以上两种情况的最大值:m[i][j] = max(m[i+1][j-wi]+vi,m[i+1][j])
代码实现:
1 #include <iostream> 2 #include<algorithm> 3 using namespace std; 4 int Knapsack(int n,int c,int w[],int v[],int m[][100]){ 5 int jMax = min(w[n]-1,c);//如果最小值是w[n]-1【最后一个物品重量减一】,说明以下j在0-jMax都放不下,如果最小值是c,那么也放不下,因为总容量比最后一个物品总容量减一还要小,肯定放不下 6 for(int j=0;j<=jMax;j++)m[n][j]=0;//容量为j【0<=j<=jMax,jMax为第n个物品的重量减一和背包容量c取最小值】,可选物品只有第n个物品的时候,背包肯定放不下,价值全部置0 7 for(int j=w[n];j<=c;j++)m[n][j]=v[n];//放的下,价值置为v[n],即第n个商品自己的价值 8 for(int i=n-1;i>1;i--){ 9 int jMax = min(w[i]-1,c); 10 for(int j=0;j<=jMax;j++)m[i][j]=m[i+1][j];//放不下,待选物品变为i+1,i+2,...n,价值不变 11 for(int j=w[i];j<=c;j++)m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);//放得下,取最大值 12 } 13 m[1][c]=m[2][c]; 14 if(c>=w[1])m[1][c]=max(m[1][c],m[2][c-w[1]]+v[1]); 15 return m[1][c]; 16 } 17 18 int main(){ 19 int n,c;//n件物品、背包容量为c, 20 cin>>n>>c; 21 int w[n],v[n]; 22 for(int i=1;i<=n;i++){ 23 cin>>w[i]>>v[i]; 24 } //输入每件物品的重量和价值 25 int m[100][100]; 26 cout<<Knapsack(n,c,w,v,m); 27 return 0; 28 }
posted on 2018-11-24 17:12 huangroumin 阅读(489) 评论(0) 编辑 收藏 举报