一、一维数组版本
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n; //n类物品
int m; //背包上限为m
int v[N][N]; //体积
int w[N][N]; //价值
int s[N]; //每类物品的个数
int f[N]; //dp数组,最大值
//分组背包
int main() {
//优化输入
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; i++) {//枚举每类物品,比如:水果,蔬菜,肉类...
cin >> s[i]; //此类物品中物品的个数,比如2,可能是苹果、香蕉
for (int j = 1; j <= s[i]; j++)//读入苹果、香蕉的体积和价值
cin >> v[i][j] >> w[i][j];
}
for (int i = 1; i <= n; i++) //遍历每类物品
for (int j = m; j >= 0; j--) //遍历每个可能的体积
for (int k = 1; k <= s[i]; k++)//遍历第k个物品
if (v[i][k] <= j)
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
cout << f[m] << endl;
return 0;
}
二、思路
-
状态表示:
f[i][j]从前i组物品中选择且总体积不大于j的最大价值 -
状态计算:
- 针对第
i组物品,将整个状态划分成s[i]+1类: - 不选第
i组物品:f[i][j] = f[i-1][j] - 选第
i组物品的第一个物品:f[i][j] = f[i-1][j-v[1]]+w[1] - 选第
i组物品的第二个物品:f[i][j] = f[i-1][j-v[2]]+w[2] - 选第
i组物品的第s[i]个物品:f[i][j] = f[i-1][j-v[s[i]]+w[s[i]]最终就是该物品数量的最大限制。十分类似于完全背包问题 - 故状态转移方程为:
f[i][j]=max(f[i][j], f[i-1][j-v[k]]+w[k])), k=0, 1, 2,...s[i]
- 针对第
-
状态初始化:
f[0][0~m] = 0表示在选择0件物品时对于任何体积来讲,其最大价值均为0
同理,分组背包问题也是可以从二维优化到一维的。其实只需要谨记一点:
当我们当前状态需要用上层状态进行转移时,就从大到小枚举体积
当我们当前状态需要用本层状态进行转移时,就从小到大枚举体积
三、二维数组版本
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n; //n组物品
int m; //背包容量
int f[N][N]; //二维dp结果记录数组
int v[N][N]; //体积,一维:哪一组,二维:第几个,值:体积是多少
int w[N][N]; //价值,一维:哪一组,二维:第几个,值:价值是多少
int s[N]; //个数,记录每一组的物品个数
int main() {
//优化输入
ios::sync_with_stdio(false);
cin >> n >> m;
//读入每一组物品
for (int i = 1; i <= n; i++) {
//个数
cin >> s[i];
//每一个的体积和价值
for (int j = 1; j <= s[i]; j++) cin >> v[i][j] >> w[i][j];
}
//遍历每一组
for (int i = 1; i <= n; i++)
//空间从0到上限
for (int j = 0; j <= m; j++) {
f[i][j] = f[i - 1][j]; //第i组啥也不要
for (int k = 1; k <= s[i]; k++) //从1个~s[i]个依次讨论
if (j >= v[i][k]) //需要能装的下
f[i][j] = max(f[i][j], f[i - 1][j - v[i][k]] + w[i][k]);
}
//输出
printf("%d",f[n][m]);
return 0;
}