核心:问题分解、状态转移
1 问题及求解
一堆物品,编号1,2,…,n,重量w1,w2,…,wn,价值v1,v2,…,vn,现在有一个包,能承受的最大重量为w,问选装哪几件物品,能带走的价值最大?
核心:问题分解、状态转移
分解:对每个物品就两种情况,要么装,要么不装,那么我们从最后一个物品n开始逐个考察。
状态转移:对第i个物品,当前背包容量为j,当前背包内最大价值为val[i][j],
如果它的重量比当前背包容量大,放弃,看下一个,即 val[i][j]=val[i-1][j];
如果它的重量不大于当前背包容量,那装还是不装,按总价值大的来,
i)不装的话,最大总价值和前面一样,背包容量不变,即a = val[i-1][j];
ii)装的话,最大总价值为前面最大总价值加上当前价值,背包容量减去该物品质量,即 b = val[i-1][j-w[i]]+v[i];
则,val[i][j]=max(a,b).
2 示例
编号1-6的物品信息为:重量数组w = {4, 6, 2, 2, 5, 1},价值数组v = {8, 10, 6, 3, 7, 2}。求背包容量C = 12时,选哪几件物品总价值最大?最大价值是多少?
求C=12的过程依赖于前面各个过程结果,列出价值矩阵如下(自下而上、自左向右求得),所以当c=12时的最大价值为24。
那具体放入了那几件物品呢?看最后一列,从最后一个逐个和上一个值做比较,如果相等,说明该物品没有放入背包,如果不等,则放入了背包。容易看出背包中的物品为第3,2,1号。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
1 | 0 | 0 | 0 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 |
2 | 0 | 0 | 0 | 8 | 8 | 10 | 10 | 10 | 10 | 18 | 18 | 18 |
3 | 0 | 6 | 6 | 8 | 8 | 14 | 14 | 16 | 16 | 18 | 18 | 24 |
4 | 0 | 6 | 6 | 9 | 9 | 14 | 14 | 17 | 17 | 19 | 19 | 24 |
5 | 0 | 6 | 6 | 9 | 9 | 14 | 14 | 17 | 17 | 19 | 21 | 24 |
6 | 2 | 6 | 8 | 9 | 11 | 14 | 16 | 17 | 19 | 19 | 21 | 24 |
3 C++实现
循环或递归均可,注释已标注
a.循环实现
#include<iostream>
using namespace std;
int main(){
int N=6;
int C=12;
int v[N+1]={0,8,10,6,3,7,2};
int w[N+1]={0,4,6,2,2,5,1};
int val[N+1][C+1];//价值矩阵
//针对第0个物品,无论背包大小是多少,所能取得的最大价值均为0
for(int j=0;j<C+1;j++) val[0][j]=0;
for(int i=1;i<=N;i++){
for(int j=1;j<=C;j++){
//如果当前背包容量j小于当前物品i的重量,则不装i物品
if(j<w[i]) val[i][j]=val[i-1][j];
//如果当前背包容量大于等于当前物品i的重量,则选择装它或不装它中较大的值
else val[i][j]=max(val[i-1][j-w[i]]+v[i],val[i-1][j]);
}
}
cout<<val[N][C]<<endl;
//从高到低,逐个判断是否装在了背包中
//判断方法:逐个与前一个比较,若价值相等,说明该物品未装入包中
int b[N+1];
for(int i=N;i>=1;i--){
if(val[i][C]==val[i-1][C]) b[i]=0;
else{
b[i]=1;
cout<<i<<' ';
C=C-w[i];
}
}
return 0;
}
b.递归实现
#include<iostream>
using namespace std;
//递归实现,返回第1至第i个物品可装入的最大价值,当前背包容量c,w[]为重量,v[]为对应的价值
int val(int i,int c,int w[],int v[]){
//第0个物品价值为0
if(i==0) return 0;
//如果当前背包容量小于当前物品重量,则不放入,最大价值同val(i-1,c,w,v)
else if(c<w[i-1]) return val(i-1,c,w,v);
//如果当前背包容量大于等于当前物品重量,放入或不放入,看哪个价值大
else{
int a=val(i-1,c-w[i-1],w,v)+v[i-1];//放入后总价值
int b=val(i-1,c,w,v);//不放入总价值
return max(a,b);
}
}
int main(){
int v[6]={8,10,6,3,7,2};
int w[6]={4,6,2,2,5,1};
int m=6;//第m个物品
int c=12;//背包容量
cout<<val(m,c,w,v)<<endl;//最大价值
//从高到低,逐个判断是否装在了背包中
//判断方法:逐个与前一个比较,若价值不相等,说明该物品放入了包中
for(int i=6;i>=1;i--){
if(val(i,c,w,v)!=val(i-1,c,w,v)){
cout<<i<<' ';
}
}
return 0;
}
参考:
1.https://blog.csdn.net/xp731574722/article/details/70766804