AcWing 11. 背包问题求方案数
01背包,不超过,得到最大价值的所有方案的数量
题目描述
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出 最优选法的方案数。注意答案可能很大,请输出答案模 109+7 的结果。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示 方案数 模 109+7 的结果。
数据范围
0
样例
输入样例
4 5
1 2
2 4
3 4
4 6
输出样例:
2
思路:
两种状态定义方式:“恰好”和“不超过”
- 恰好:
f[i][j]
表示从前i
个物品中选且总体积恰好为j
的获得最大价值的选法的集和,属性是最大价值g[i][j]
表示从前i
个物品中选且总价值为f[i][j]
的方案的集和,属性是方案数- 初始化:
f[i][j] = -INF, f[0][0] = 0, g[0][0] = 1
- 不超过:
f[i][j]
表示从前i
个物品中选且总体积不超过j
的获得最大价值的选法的集和,属性是最大价值g[i][j]
表示从前i
个物品中选且总价值为f[i][j]
的方案的集和,属性是方案数。- 初始化:
f[i][j] = -INF, f[0][j] = 0, g[0][j] = 1
// 恰好
import java.util.*;
public class Main {
static final int N = 1010, INF = 0x3f3f3f3f, MOD = (int)(1e9 + 7);
static int[][] f = new int[N][N], g = new int[N][N];
static int n, m;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 0; i <= n; i++) {
Arrays.fill(f[i], -INF);
}
f[0][0] = 0;
g[0][0] = 1;
for (int i = 1; i <= n; i++) {
int v = sc.nextInt(), w = sc.nextInt();
for (int j = 0; j <= m; j++) {
if (j >= v) {
int max = Math.max(f[i - 1][j], f[i - 1][j - v] + w);
int cnt = 0;
if (f[i - 1][j] == max) cnt += g[i - 1][j];
if (f[i - 1][j - v] + w == max) cnt += g[i - 1][j - v];
f[i][j] = max;
g[i][j] = cnt % MOD;
} else {
f[i][j] = f[i - 1][j];
g[i][j] = g[i - 1][j];
}
}
}
int res = 0;
for (int i = 0; i <= m; i++)
res = Math.max(res, f[n][i]);
int cnt = 0;
for (int i = 0; i <= m; i++)
if (f[n][i] == res)
cnt = (cnt + g[n][i]) % MOD;
System.out.println(cnt);
}
}
// 一维
import java.util.*;
public class Main {
static final int N = 1010, INF = 0x3f3f3f3f, MOD = (int)(1e9 + 7);
static int[] f = new int[N], g = new int[N];
static int n, m;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
Arrays.fill(f, -INF);
f[0] = 0;
g[0] = 1;
for (int i = 1; i <= n; i++) {
int v = sc.nextInt(), w = sc.nextInt();
for (int j = m; j >= v; j--) {
int max = Math.max(f[j], f[j - v] + w);
int cnt = 0;
if (max == f[j]) cnt += g[j];
if (max == f[j - v] + w) cnt += g[j - v];
f[j] = max;
g[j] = cnt % MOD;
}
}
int res = 0;
for (int i = 0; i <= m; i++)
res = Math.max(res, f[i]);
int cnt = 0;
for (int i = 0; i <= m; i++)
if (f[i] == res)
cnt = (cnt + g[i]) % MOD;
System.out.println(cnt);
}
}
// 不超过
import java.util.*;
public class Main {
static final int N = 1010, INF = 0x3f3f3f3f, MOD = (int)(1e9 + 7);
static int[][] f = new int[N][N], g = new int[N][N];
static int n, m;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 1; i <= n; i++)
Arrays.fill(f[i], -INF);
Arrays.fill(g[0], 1);
for (int i = 1; i <= n; i++) {
int v = sc.nextInt(), w = sc.nextInt();
for (int j = 0; j <= m; j++) {
if (j >= v) {
int max = Math.max(f[i - 1][j], f[i - 1][j - v] + w);
int cnt = 0;
if (max == f[i - 1][j]) cnt += g[i - 1][j];
if (max == f[i - 1][j - v] + w) cnt += g[i - 1][j - v];
f[i][j] = max;
g[i][j] = cnt % MOD;
} else {
f[i][j] = f[i - 1][j];
g[i][j] = g[i - 1][j];
}
}
}
System.out.println(g[n][m]);
}
}
// 一维
import java.util.*;
public class Main {
static final int N = 1010, INF = 0x3f3f3f3f, MOD = (int)(1e9 + 7);
static int[] f = new int[N], g = new int[N];
static int n, m;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
Arrays.fill(g, 1);
for (int i = 1; i <= n; i++) {
int v = sc.nextInt(), w = sc.nextInt();
for (int j = m; j >= v; j--) {
int max = Math.max(f[j], f[j - v] + w);
int cnt = 0;
if (max == f[j]) cnt += g[j];
if (max == f[j - v] + w) cnt += g[j - v];
f[j] = max;
g[j] = cnt % MOD;
}
}
System.out.println(g[m]);
}
}
方法2:
不用g
, 用dfs考虑方案的数量
从终止状态f[n][m]
倒推至f[0][0]
即求dfs(n m)
,在深搜的过程中计算方案数
结果会TLE
,因为物品数太多了,深搜时间复杂度过高了