一、分析过程
本题是 背包DP 中的经典题型 —— 【背包DP求最优方案总数】
\(01\)背包的转移方程\(f[i,j] = max(f[i - 1,j],f[i - 1,j - v] + w)\)
其中\(g[i,j]\) 是 \(f[i,j]\)取最大值的方案数
若\(f[i,j]\)是从\(f[i - 1,j]\)转移过来的,则\(g[i,j] = g[i - 1,j]\)
若\(f[i,j]\)是从\(f[i - 1,j - v] + w\)转移过来的,则\(g[i,j] = g[i - 1,j - v]\)
若\(f[i,j]\)均能从\(f[i - 1,j]\)和\(f[i - 1,j - v] + w\)转移过来的,则\(g[i,j] = g[i - 1,j] + g[i - 1,j - v]\)
将所有的最大值所对应的方案数累加在一起,即为方案数总和
二、二维实现
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
const int MOD = 1e9 + 7;
int n, m;
int w[N], v[N];
int f[N][N], g[N][N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
//01背包求最大价值
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++) {//二维是正序遍历
f[i][j] = f[i - 1][j];
if (j >= v[i]) f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
//前0个物品中选,体积是0的情况下,最大价值就是0,一种方案
g[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
//查看一下当前的最大值,从前面哪个状态转移而来
if (f[i][j] == f[i - 1][j])
g[i][j] = (g[i][j] + g[i - 1][j]) % MOD;//叠加方案数
if (j >= v[i] && f[i][j] == f[i - 1][j - v[i]] + w[i])
g[i][j] = (g[i][j] + g[i - 1][j - v[i]]) % MOD;
}
}
int res = 0;
for (int i = 1; i <= m; i++)//遍历每一个体积
if (f[n][i] == f[n][m]) res = (res + g[n][i]) % MOD;
cout << res << endl;
return 0;
}
三、一维实现
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
const int MOD = 1e9 + 7;
int f[N]; //f[i]用来存储背包容积为i时的最大价值,
int g[N]; //g[i]用来存储背包容积为i时,获取到最大价值时的方案数
int main() {
//优化输入
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
//先初始化所有的 g[i]为 1,因为背包里什么也不装也是一种方案,方案数最小是1
for (int i = 0; i <= m; i++) g[i] = 1;
for (int i = 1; i <= n; i++) {
int v, w;
cin >> v >> w;
for (int j = m; j >= v; j--) {
//求出装新物品时的总价值,与不装新物品时作对比
int value = f[j - v] + w;
//如果装新物品的总价值更大,那么用f[j−v]+w来更新f[j](经典01背包)
if (value > f[j]) {
f[j] = value;
//用g[j−v]更新g[j]
g[j] = g[j - v];
} else if (value == f[j])
//如果总价值相等,那么最大价值的方案数就多了 g[j−v]种
g[j] = (g[j] + g[j - v]) % MOD;
}
}
printf("%d", g[m]);
return 0;
}