w[i]表示第i个物品的体积,v[i]表示第i个物品的价值。
dp[i][j]表示背包容量为j的情况下,从前0-i个物品中可以选到的最大价值。
一、0-1背包
二维数组
int n,m;
cin >> n>>m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (j < w[i])
dp[i]= dp[i - 1][j];
else
dp[i]=max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
}
}
一维滚动数组
将原二维数组的每一行看做同一个数组不同时间步的呈现。相当于不需要存储每个时间步的数据了,直接不断更新一个数组就行了。
for (int i = 1; i <= n; i++) {
for (int j = m; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
二、完全背包
二维数组
//第0行和第0列全部为0
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= w[i]) {
dp[i][j] = max(dp[i][j], dp[i][j - w[i]] + v[i]);
}
}
}
一维滚动数组
for (int i = 1; i <= n; i++) {
for (int j = w[i]; j <= m; j++) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
三、多重背包
普通版本
for (int i = 1; i <= n; i++) {
for (int j = m; j >= w[i]; j--) {
// 多遍历一层物品数量
for (int k = 1; k * w[i] <= j && k <= cnt[i]; k++) {
dp[j] = max(dp[j], dp[j - k * w[i]] + k * v[i]);
}
}
}
二进制优化版本
int n, m, pos = 0;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int v1, w1, s, k = 1;
cin >> v1 >> w1 >> s;
while (k <= s) {
v[++pos] = v1 * k;
w[pos] = w1 * k;
s -= k;
k *= 2;
}
if (s > 0) {
v[++pos] = v1 * s;
w[pos] = w1 * s;
}
}
for (int i = 1; i <= pos; i++) {
for (j = m; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
四、分组背包
0-n个物品分属不同组,每个组只能取一个。
for (int k = 1; k <= ts; k++) // 循环每一组
for (int i = m; i >= 0; i--) // 循环背包容量
for (int j = 1; j <= cnt[k]; j++) // 循环该组的每一个物品
if (i >= w[t[k][j]]) // t[k][j]表示第k组第j个物品在全部物品中的编号
dp[i] = max(dp[i],dp[i - w[t[k][j]]] + c[t[k][j]]); //状态转移方程