背包问题是一种比较经典的动态规划问题,在这里把几种背包都讨论一下。
0-1背包
给你一个可装载重量为 c
的背包和 n
个物品,每个物品有重量和价值两个属性。其中第 i
个物品的重量为 w[i]
,价值为
v[i]
,现在让你用这个背包装物品,最多能装的价值是多少?
如果使用动态规划分析,首先看有什么状态,对于每件物品,有选和不选两个状态。
定义\(dp[i][j]\)为对于前\(i\)个物品,当前背包容量为\(j\)时的最大价值,很明显要返回\(dp[n][c]\)。
若不选第\(i\)件物品来说,\(dp[i][j]=dp[i-1][j]\),等价于只考虑前\(i-1\)件物品,容量为\(j\)时的最大值;
若选第\(i\)件物品,\(dp[i][j]=dp[i-1][j-w[i]]+v[i]\),考虑前面\(i-1\)件物品,容量因为选了当前的物品,所以剩余容量就为\(j-v[i]\)。
还有一个额外的条件为,若要选则当前的第\(i\)件物品,背包的剩余容量\(j\)应该要\(\ge
w[i]\)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.util.*; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); int c = in.nextInt(); int[] w = new int[n]; int[] v = new int[n]; for(int i = 0; i < n; i++) { w[i] = in.nextInt(); v[i] = in.nextInt(); } System.out.println(zeroOneBackpack(n,c,v,w)); in.close(); } public static int zeroOneBackpack(int n, int c, int[] v, int[] w) { int[][] dp = new int[n + 1][c + 1]; for(int i = 1; i <= n; i++) { for(int j = 1; j <= c; j++) { if(j < w[i - 1]) { dp[i][j] = dp[i - 1][j]; }else{ dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]); } } } return dp[n][c]; } }
|
完全背包
有一个背包,最大容量为 c
,有一系列物品
,每个物品的重量为
weight[i]
,每个物品的数量无限。请问背包的最大价值是?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.util.*; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = in.nextInt(); int c = in.nextInt(); int[] w = new int[n]; int[] v = new int[n]; for(int i = 0; i < n; i++) { w[i] = in.nextInt(); v[i] = in.nextInt(); } System.out.println(completeBackpack(n,c,v,w)); in.close(); } public static int completeBackpack(int n, int c, int[] v, int[] w) { int[][] dp = new int[n + 1][c + 1]; for(int i = 1; i <= n; i++) { for(int j = 1; j <= c; j++) { if(j < w[i - 1]) { dp[i][j] = dp[i - 1][j]; }else{ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - w[i - 1]] + v[i - 1]); } } } return dp[n][c]; } }
|
完全背包问题还有以下变种,即求给定的物品组成容量为c
的背包的方案数。
有一个背包,最大容量为 c
,有一系列物品
,每个物品的重量为
weight[i]
,每个物品的数量无限。请问有多少种方法,能够把背包恰好装满?
状态有两个,就是「背包的容量」和「可选择的物品」,选择还是「装进背包」或者「不装进背包」。
定义\(dp[i][j]\)为只使用前\(i\)个物品,当背包容量为\(j\)时的方法总数。
显然\(dp[0][...]=0\),因为不使用物品,也就不存在方法,\(dp[...][0]=1\),因为背包容量为0时,只有什么都不选这一种方法,最后要求得的就是\(dp[weight.length][c]\)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import java.util.*; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int c = in.nextInt(); int n = in.nextInt(); int[] weight = new int[n]; for(int i = 0; i < n; i++) { weight[i] = in.nextInt(); } System.out.println(completeBackpackScheme(c, weight)); in.close(); } public static int completeBackpackScheme(int c, int[] weight) { int n = weight.length; int[][] dp = int[n + 1][c + 1]; for (int i = 0; i <= n; i++) dp[i][0] = 1; for (int i = 1; i <= n; i++) { for (int j = 1; j <= c; j++) if (j - weight[i - 1] >= 0) dp[i][j] = dp[i - 1][j] + dp[i][j - weight[i - 1]]; else dp[i][j] = dp[i - 1][j]; } return dp[n][c]; } }
|
完全背包的关键点就在于如果选择了第\(i\)件物品,还是可以继续选择第\(i\)件物品,所以\(dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - w[i -
1]] + v[i - 1]);\)