我们先来看看经典的分组背包问题。
什么是分组背包问题?
有一个体积为V的背包,有N组物品,从N组物品中每组只能选择一件物品,我们要达到的要求是让物品的总体积不超过背包的体积V,并且让整个背包中所装的物品价值最大。
背包问题应该怎么做呢?
写出他的状态表示以及状态转移方程。
该问题的集合是只从前I组物品中每组选择一件物品且总体积小于V的所有集合。
属性就是求其价值的最大值。
在第I组物品中有什么样的方式呢?
不选第I组的物品。
选第i组第一个物品。
选第i组第k个物品。
发现什么了吗?
我们只需要在每次转移中对单个的第K个物品进行计算就可以,其实在我看来也可以用0,1背包来理解。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int v[N][N], w[N][N];//v与w都用二维,因为每一个组别都要分别存放他们的组别标识..
int f[N];//状态转移中用的f数组。。
int s[N];//存储每个组别中有多少个物品。
int main()
{
int n,m;
cin>>n>>m;//请注意这里的N是N组物品而不是,n件物品。
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++)
{
for(int j=m;j>=0;j--)
{
for(int k=1;k<=s[i];k++)
{
if(v[i][k]<=j)//判断能否进行。
f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
}
}
}
cout<<f[m]<<"\n";
return 0;
}
让我们回到小明的通天背包,很明显发现这个问题中既没有给我们分组的组数又没有在第一时间告诉我们每一个组都有几个物品,但其实还是挺简单的。
只要我们把之前需要的东西预处理好就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N = 1100;
int s[N];
int v[N][N];
int w[N][N];
int f[N];
int main() {
int n, m;
cin >> m >> n;
int cnt = 0;
for (int i = 1; i <= n; i++) {
int a, b, c; // 重量,价值,组别。
cin >> a >> b >> c;
if (s[c] == 0) cnt++; // 如果该组别从未出现过则组别数加一。
s[c]++; // 组别中的物品数字标签。
v[c][s[c]] = a; // 在该组别中存入两个值价值和体积。
w[c][s[c]] = b;
}
// 背包模板.
for (int i = 1; i <= cnt; i++) {
for (int j = m; j >= 0; j--) {
for (int k = 0; k <= s[i]; k++) { // 这里是 k <= s[i] 而不是 i。
if (j >= v[i][k]) {
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
}
}
}
}
cout << f[m] << "\n";
return 0;
}