背包问题——分组背包
分组背包
1.定义
分组背包,通俗的讲就是,给你N组物品,然后每一组你至多选择一个物品(也可以不选),每个物品都有自己的体积和价值,现在给你一个容里为M的背包,让你用这个背包装物品,使得物品价值总和最大.
2.讲解
其实就类似于01背包,对于一个物品有两种决策选或不选,但是分组背包是在01背包的基础上对物品进行了分组,并且每一组只能最多选择一个物品,所以我们不妨用01背包的思想去思考分组背包.
分析:我们设f[i][j]为当前考虑到了第i组物品,剩余容里为j的背包能装物品的最大价值,那么很容易想到我们需要去枚举第i组物品,考虑选哪一个物品时最优的(或者不选),状态转移方程就是i f ( j > = v [ i ] [ k ] ) f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ j − v [ i ] [ k ] ] + w [ i ] [ k ] ),v[i][k]和w[i][k]分别表示第i组物品中第k个物品的体积和价值
代码:
for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) for(int k=1;k<=s[i];k++)//s[i]表示第i组物品的个数 if(j>=v[i][k])//剩余的背包容量j大于第i组的第k个物品的体积 { f[i][j] = max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]); }
这里我们还可以对空间进行优化,我们可以观察到,f[i][…]只会用到f[i-1][…]的值,所以数组的第一维的空间完全可以用滚动数组的方式处理掉,但是如何不影响状态转移呢,我们来看滚掉之后的状态转移方程,
f[j] = max(f[j],f[j-v[i][k]]+w[i][k]);
这里的max里面的f [ j ] 和 f [ j − v [ i ] [ k ] ] 其实是f [ i − 1 ] [ j ] 和 f [ i − 1 ] [ j − v [ i ] [ k ] ],而不是f [ i ] [ j ] 和 f [ i ] [ j − v [ i ] [ k ] ],所以我们需要对体积的遍历做一些修改,从大到小循环,如果还是从小到大循环的话,那么这里的f [ j ] 和 f [ j − v [ i ] [ k ] ] 的含义就有可能是f [ i ] [ j ] 和 f [ i ] [ j − v [ i ] [ k ] ],而不是我们需要的f [ i − 1 ] [ j ] 和 f [ i − 1 ] [ j − v [ i ] [ k ] ],可以模拟一下就明白了,只靠想的话有点抽象.
5743: 分组背包
#include<bits/stdc++.h> using namespace std; long long int dp[1005]; vector<int> w[40]; vector<int> v[40]; int n,m,t; //n个物品,m容量,t组 int main() { cin>>m>>n>>t; for(int i=1;i<=n;i++) { int wi,vi,ti; cin>>wi>>vi>>ti; w[ti].push_back(wi); v[ti].push_back(vi); } for(int i=1;i<=t;i++) { for(int j=m;j>=0;j--) { for(int k=0;k<w[i].size();k++) { if(j>=w[i][k]) dp[j] = max(dp[j],dp[j-w[i][k]]+v[i][k]); } } } cout<<dp[m]; return 0; }