$\Huge\color{orchid}{点击此处获取更多干货}$
动态规划
分组的背包问题算是0-1背包问题的变种,在每个物品只能选1次或不选的前提下,多了一个分组条件,组内的物品只能选其中一件,或者都不选,因此这次我们将$dp(i,j)$的$i$定义成“从前$i$组物品中选择”,$j$的定义仍然还是占用的最大容量。下面是$dp$图表:
可以直接套用0-1背包问题的一维$dp$表和倒序枚举,不过枚举的终点得是0,因为每一组内到底选哪个物品,事先是不知道的,需要挨个去枚举这些物品,然后判断$j-v$是否不小于0,才能去进行递推。
另外,按照之前的规律,第$i$组物品的信息可以等到第$i$轮迭代再给出,但是本帖并未找到合适的给出方式,因此就简单的用二维数组表示了,有知道的可以在评论区留言
C++ 代码
#include <iostream>
using namespace std;
struct Obj {
int v, w;
Obj() {}
Obj(int _v, int _w) : v(_v), w(_w) {}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
Obj** packs = new Obj * [n];
int* dp = new int[m + 1], * s = new int[n], v, w;
for (int i = 0; i < n; i++) {
cin >> s[i];
packs[i] = new Obj[s[i]]; //二维数组的本质就是一维数组的数组,而这些一维数组的长度可以互不相同
for (int j = 0; j < s[i]; j++) {
cin >> v >> w;
packs[i][j] = Obj(v, w);
}
}
for (int i = 0; i < n; i++) { //像0-1背包那样递推
for (int j = m; j >= 0; j--) {
for (int k = 0; k < s[i]; k++) { //枚举每个物品
if (j - packs[i][k].v >= 0) { //能装入才能执行递推
dp[j] = max(dp[j], dp[j - packs[i][k].v] + packs[i][k].w);
}
}
}
}
cout << dp[m] << endl; //输出和0-1背包问题一样
delete[] dp, s;
for (int i = 0; i < n; i++) {
delete[] packs[i];
}
delete[] packs;
return 0;
}