如果物品可以拆分,则问题称为背包问题,适合使用贪心算法
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问:应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
设(y1,y2,…,yn)是 (3.4.1)的一个最优解.则(y2,…,yn)是下面相应子问题的一个最优解:
证明:使用反证法。若不然,设(z2,z3,…,zn)是上述子问题的一个最优解,而(y2,y3,…,yn)不是它的最优解。显然有 ∑vizi > ∑viyi (i=2,…,n) 且 w1y1+ ∑wizi<= c 因此 v1y1+ ∑vizi (i=2,…,n) > ∑ viyi, (i=1,…,n) 说明(y1,z2, z3,…,zn)是(3.4.1)0-1背包问题的一个更优解,导出(y1,y2,…,yn)不是背包问题的最优解,矛盾。
递推关系:
设所给0-1背包问题的子问题
的最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优值。由0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式:
注:(3.4.3)式此时背包容量为j,可选择物品为i。此时在对xi作出决策之后,问题处于两种状态之一: (1)背包剩余容量是j,没产生任何效益; (2)剩余容量j-wi,效益值增长了vi ; (3) 对于最后一个物品n , 如果 j>=Wn,则肯定装入, 获得价值Vn; 如果0<=j < Wn,则无法装入, 获得的价值为 0 。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 5 #define NUM 50 //物品数量的上界 6 #define CAP 1500 //背包容量的上界 7 int v[NUM]; //物品的重量 8 int w[NUM]; //物品的价值 9 int p[NUM][CAP]; //用于递归的数组。 10 //形参 c 是背包的容量 W,n是物品的数量。 11 void knapsack(int c, int n) 12 { 13 //计算递推边界 14 int jMax=min(w[n]-1,c); //分界点。 15 for( int j=0; j<=jMax; j++) 16 p[n][j]=0; 17 for( int j=w[n]; j<=c; j++) 18 p[n][j]=v[n]; 19 for( int i=n-1; i>1; i--)//计算递推式 20 { 21 jMax=min(w[i]-1,c); 22 for( int j=0; j<=jMax; j++) 23 p[i][j]=p[i+1][j]; 24 for(int j=w[i]; j<=c; j++) 25 p[i][j]=max(p[i+1][j], p[i+1][j-w[i]]+v[i]); 26 } 27 p[1][c]=p[2][c]; //计算最优值。 28 if (c>=w[1]) 29 p[1][c]=max(p[1][c], p[2][c-w[1]]+v[1]); 30 } 31 //形参数组 x 是解向量。 32 void traceback( int c, int n, int x[]) 33 { 34 for(int i=1; i<n; i++) 35 { 36 if (p[i][c]==p[i+1][c]) x[i]=0; 37 else { x[i]=1; c-=w[i]; } 38 } 39 x[n]= (p[n][c]) ? 1:0; 40 } 41 42 int main () 43 { 44 int x[NUM]; 45 int W; 46 int n; 47 while (scanf("%d", &W) && W) 48 { 49 scanf("%d", &n); 50 for (int i=1; i<=n; i++) 51 scanf("%d%d", &w[i], &v[i]); 52 memset (p, 0, sizeof(p)); 53 knapsack(W, n); 54 printf("%d\n", p[1][W]); 55 traceback(W, n, x); 56 for (int i=1; i<=n; i++) 57 if (x[i]) printf("%d ", i); 58 printf("\n"); 59 } 60 return 0; 61 }