文章目录
- 01背包
- 例题:2. 01背包问题
- 完全背包
- 例题:3. 完全背包问题
- 多重背包
- 例题:4. 多重背包问题 I
- 例题:5. 多重背包问题 II(数据范围较大:二进制优化)
- 分组背包
- 例题:9. 分组背包问题
01背包
例题:2. 01背包问题
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int[] v = new int[n], w = new int[n];
for (int i = 0; i < n; ++i) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
int[] dp = new int[m + 1];
for (int i = 0; i < n; ++i) { // 枚举物品
for (int j = m; j >= v[i]; --j) { // 枚举背包
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
System.out.println(dp[m]);
}
}
完全背包
例题:3. 完全背包问题
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int[] v = new int[n], w = new int[n];
for (int i = 0; i < n; ++i) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
}
int[] dp = new int[m + 1];
for (int i = 0; i < n; ++i) { // 枚举物品
for (int j = v[i]; j <= m; ++j) { // 枚举背包
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
System.out.println(dp[m]);
}
}
多重背包
例题:4. 多重背包问题 I
把 多重背包拆分成 01背包即可。
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int[] v = new int[n], w = new int[n], s = new int[n];
for (int i = 0; i < n; ++i) {
v[i] = sc.nextInt();
w[i] = sc.nextInt();
s[i] = sc.nextInt();
}
int[] dp = new int[m + 1];
for (int i = 0; i < n; ++i) { // 枚举物品
for (int k = 0; k < s[i]; ++ k) { // 枚举 k 个物品
for (int j = m; j >= v[i]; --j) { // 枚举背包
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
}
System.out.println(dp[m]);
}
}
例题:5. 多重背包问题 II(数据范围较大:二进制优化)
这一题和上一题唯一不同的就是数据范围。
因此,本题考查的重点是:多重背包的二进制优化方法
核心思想
是: 将每个物品的数量 x 分成若干组,每组的数量都是 2 的幂次。(因为分成这样若干个组之后,它们之间可以组合成 0 ~ s 的任意数量。)
将 1023 数量分组打包成 1,2,4,8,… 512。
这样就将一个循环的
O
(
n
)
O(n)
O(n) 优化成了
O
(
log
n
)
O(\log{n})
O(logn)
那不能正好分成 2 的幂该怎么办呢?看下面这个例子:
最后还剩多少,就设置成多少。
import java.util.*;
public class Main {
final static int N = 12010;
static int[] v = new int[N], w = new int[N];
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int cnt = 0;
for (int i = 0; i < n; ++i) {
int a = sc.nextInt(), b = sc.nextInt(), s = sc.nextInt();
int k = 1;
// k = 1,2,4,...
while (k <= s) {
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k *= 2;
cnt++;
}
// 如果 s 没有被若干个 k 分完
if (s > 0) {
v[cnt] = a * s;
w[cnt] = b * s;
cnt++;
}
}
n = cnt;
int[] dp = new int[m + 1];
// 01背包模板
for (int i = 0; i < n; ++i) {
for (int j = m; j >= v[i]; --j) {
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}
System.out.println(dp[m]);
}
}
分组背包
例题:9. 分组背包问题
分组背包的枚举顺序为:
每一组 —— 背包容量 —— 每一组中的物品。
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
int[] dp = new int[m + 1];
List<int[][]> goods = new ArrayList<>();
for (int i = 0; i < n; ++i) {
int s = sc.nextInt();
int[][] ths = new int[s][2];
for (int j = 0; j < s; ++j) {
ths[j][0] = sc.nextInt();
ths[j][1] = sc.nextInt();
}
goods.add(ths);
}
for (int k = 0; k < n; ++k) { // 枚举每一组
int[][] ths = goods.get(k);
for (int j = m; j >= 0; --j) { // 枚举背包容量
for (int[] th : ths) { // 枚举该组的每一个物品
if (j >= th[0]) {
dp[j] = Math.max(dp[j], dp[j - th[0]] + th[1]);
}
}
}
}
System.out.println(dp[m]);
}
}
更多相关内容可见:【算法】01背包和完全背包