题目描述
01背包问题
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤100
0
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
解决思路
01背包问题可以用动态规划来求解
按照之前文章提到的动态规划的解题模板:
(1)确定数组元素含义
int dp[][]=new int[N+1][V+1]
dp[i][j] 表示背包容积为j数量为i时最大的价值。
(2) 确定状态转移方程
当计算第i个物品时无非两种情况,第一种是放该物品,第二种是不放该物品。
第一种情况:dp[i][i]=dp[i-1][j]
第二种情况 dp[i][j]=dp[i-1][j-v[i]]+w[i],这种情况的前提条件是j>=v[i]
因此 dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-v[j]]+w[j])
(3)确定初始值
dp[0][0…j]=0, dp[0…i][0]即不放任何物品,体积为0时,总价值为0;
因为java中new出来的数组初始值为0,因此这里不需要任何的操作
(4)根据初始值和状态转移方程确定数组元素的值
(5)返回结果 dp[N][V]
(6)考虑状态压缩
使用二维数组的代码如下:
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner scanner =new Scanner(System.in);
int N=scanner.nextInt();//N行 物品数量
int V=scanner.nextInt();//背包容积
int v[]=new int[N+1];
int w[]=new int[N+1];
for(int i=1;i<=N;i++){
v[i]=scanner.nextInt();
w[i]=scanner.nextInt();
}
int dp[][]=new int[N+1][V+1];//状态数组
dp[0][0]=0;
for(int i=1;i<=N;i++){
for(int j=1;j<=V;j++){
dp[i][j]=dp[i-1][j];
if(j-v[i]>=0){
dp[i][j]=Math.max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
}
}
}
System.out.println(dp[N][V]);
}
}
压缩为一维数组与二维数组不同之处在于遍历的时候需要逆序,是为了第i轮中不覆盖到第i-1轮中的结果。可以参考下文:
对使用倒序的一维数组解决0/1背包问题的理解
压缩为意味数组的代码如下:
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner scanner =new Scanner(System.in);
int N=scanner.nextInt();//N行 物品数量
int V=scanner.nextInt();//背包容积
int v[]=new int[N+1];
int w[]=new int[N+1];
for(int i=1;i<=N;i++){
v[i]=scanner.nextInt();
w[i]=scanner.nextInt();
}
int dp[]=new int[V+1];//用来表示该体积下可以容纳物品的最大总价值
for(int i=1;i<=N;i++){
for(int j=V;j>=v[i];j--){
dp[j]=Math.max(dp[j],dp[j-v[i]]+w[i]);
}
}
System.out.println(dp[V]);
}
}
更进一步,记录一下中兴的在线笔试题计算反应堆的最大能量,该题目是01背包问题的变形,不再做过多的赘述
public class Test2_01 {
public static void main(String[] args) {
int reactorCap = 100; // 反应堆的容量(V)
int numberOfRadLiquid = 5; // 现有小瓶数量(N)
int criticalMass = 15; // 反应堆的最大临界质量(M)
int volume[] = { 50, 40, 30, 20, 10 };// 体积
int masses[] = { 1, 2, 3, 9, 5 };// 质量
int energies[] = { 300, 480, 270, 200, 180 }; // 能量
int dp[][][]=new int[numberOfRadLiquid+1][criticalMass+1][reactorCap+1];
for(int i=1;i<=numberOfRadLiquid;i++) {
for(int j=1;j<=criticalMass;j++) {
for(int k=1;k<=reactorCap;k++) {
dp[i][j][k]=dp[i-1][j][k];
if(j>=masses[i-1]&&k>=volume[i-1]) {
dp[i][j][k]=Math.max(dp[i][j][k], dp[i-1][j-masses[i-1]][k-volume[i-1]]+energies[i-1]);
}
}
}
}
System.out.print(dp[numberOfRadLiquid][criticalMass][reactorCap]);
}
}
空间压缩
public class Test2_02 {
public static void main(String[] args) {
int reactorCap = 100; // 反应堆的容量(V)
int numberOfRadLiquid = 5; // 现有小瓶数量(N)
int criticalMass = 15; // 反应堆的最大临界质量(M)
int volume[] = { 50, 40, 30, 20, 10 };// 体积
int masses[] = { 1, 2, 3, 9, 5 };// 质量
int energies[] = { 300, 480, 270, 200, 180 }; // 能量
int dp[][]=new int[criticalMass+1][reactorCap+1];
for(int i=1;i<=numberOfRadLiquid;i++) {
for(int j=criticalMass;j>=masses[i-1];j--) {
for(int k=reactorCap;k>=volume[i-1];k--) {
dp[j][k]=Math.max(dp[j][k], dp[j-masses[i-1]][k-volume[i-1]]+energies[i-1]);
}
}
}
System.out.print(dp[criticalMass][reactorCap]);
}
}