超详解动态规划之0-1背包问题
问题:
问题:有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 w[i],价值是 p[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
举例:假设v=20,w={5,6,3,7,8},p={6,7,4,8,9},则最大价值为23
逆向构造最优解输出哪些物品放入了背包
输入
5
20
5 6 3 7 8
6 7 4 8 9
输出
23
1 4 5
算法设计:
有n个物品,每个物品的重量为w[i],价值为v[i],背包的容量为w.选若干个物品放入背包,在不超过容量的前提下使获得的价值最大.
(1)确定合适的数据结构
采用一维数组w[i],v[i]来记录第i个物品的重量和价值;二维数组c[i][j]表示前i件物品放入一个容量为j的背包可以获得的最大价值.
(2)初始化
初始化c[][]数组0行0列为0:c[0][j]=0,c[i][0]=0;其中i=0,1,2,3…,n,j=0,1,2,3…w.
(3)循环阶段
①按照递归式计算第一个物品的处理情况,得到c[1][j],j=1,2,…w.
②按照递归式计算第二个物品的处理情况,得到c[2]j],j=1,2,…w.
③以此类推,按照递归式计算第n个物品的处理情况,得到c[n][j],j=1,2,3…,w.
(4)构造最优解
c[n][w]就是不超过背包容量能放入物品的最大价值.如果还想知道具体放入了哪些物品,就需要根据c[][]数组逆向构造最优解.我们可以用个一维数组x[i]来存储解向量.
①首先i=n,j=w,如果c[i][j]>c[i-1][j],则说明第n个物品放入了背包,令x[n]=1,j-=w[n];如果c[i][j]≤c[i-1][j],则说明第n个物品没有放入背包,令x[n]=0.
②i–,继续查找答案.
③直到i=1处理完毕.
这时已经得到了解向量(x[1],x[2],…x[n]),可以直接输出该解向量,也可以仅把x[i]=1的物品序号i输出.
代码:
import java.util.Scanner;
public class text1 {
public static void main(String args[]){
int n;//物品个数
int w;//背包的容量
int i,j;
Scanner sc=new Scanner(System.in);
System.out.println("请输入物品的个数");
n=sc.nextInt();
int []V=new int[n+1];//价值数组
System.out.println("请输入背包的容量");
w=sc.nextInt();//重量数组
int []W=new int[w+1];
int []x=new int[n+1];//逆向构造最优解的时候表示,该物品有没有放入背包
int [][]c=new int[n+1][w+1];//表示前i个物品放入容器为j的背包获得的最大价值
System.out.println("请输入每个物品的重量");
for ( i=1;i<=n;i++){
W[i]=sc.nextInt();
}
System.out.println("请输入每个物品的价值");
for ( i=1;i<=n;i++){
V[i]=sc.nextInt();
}
for ( i = 0; i <=n ; i++) {
c[i][0]=0;//初始化第0列为零
}
for ( j=0;j<=w;j++){
c[0][j]=0;//初始化第0行为0;
}
for ( i=1;i<=n;i++) //计算c[i][j]
for( j=1;j<=w;j++)
if(j<W[i]) //当物品的重量大于背包的重量,则不放入此物品
c[i][j] = c[i - 1][j];
else //否则比较此物品放与不放是否能使得背包内的价值最大
c[i][j]=Math.max(c[i-1][j],c[i-1][j-W[i]]+V[i]);
System.out.println(c[n][w]);
//逆向构造最优解
j=w;
for(i=n;i>0;i--){
if(c[i][j]>c[i-1][j]){
x[i]=1;
j-=W[i];
}
else
x[i]=0;
}
System.out.println("装入背包的物品为:");
for(i=1;i<=n;i++){
if(x[i]==1){
System.out.print(i+" ");
}
}
}
}