题目描述
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。
接下来有 N 组数据:
每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤100
0<Si≤100
0<vij,wij≤100
样例
输入样例
3 5
2
1 2
2 4
1
3 4
1
4 5
输出样例:
8
算法1
(暴力枚举) $O(n*m*k)$
#include <iostream>
#include <vector>
using namespace std;
int main(){
int n, m;
cin>>n>>m;
vector<vector<int>> v(1, vector<int>(m, 0)), w(1, vector<int>(m, 0));
vector<int> s(1, 0);
for(int i = 1; i <= n; ++i){
int si = 0;
cin>>si;
s.push_back(si);
vector<int> vi(si + 1, 0), wi(si + 1, 0);
for(int j = 1; j <= si; ++j){
cin>>vi[j]>>wi[j];
}
v.push_back(vi);
w.push_back(wi);
}
vector<vector<int>> f(n + 1,vector<int>(m + 1,0));
for(int i = 1; i <= n; ++i){
for(int j = 0; j <= m; ++j){
for(int k = 0; k <= s[i]; ++k)
// j >= v[i][k] && 条件不能写在for()这里,它一旦不满足,就会跳出循环,使得 k 个物品不能全部遍历
if( j >= v[i][k] )
f[i][j] = max(f[i][j], f[i - 1][j - v[i][k]] + w[i][k]);
}
}
cout<<f[n][m]<<endl;
return 0;
}
算法2
优化空间
时间复杂度
C++ 代码
#include <iostream>
#include <vector>
using namespace std;
int main(){
int n, m;
cin>>n>>m;
vector<vector<int>> v(1, vector<int>(m, 0)), w(1, vector<int>(m, 0));
vector<int> s(1, 0);
for(int i = 1; i <= n; ++i){
int si = 0;
cin>>si;
s.push_back(si);
vector<int> vi(si + 1, 0), wi(si + 1, 0);
for(int j = 1; j <= si; ++j){
cin>>vi[j]>>wi[j];
}
v.push_back(vi);
w.push_back(wi);
}
vector<int> f(m + 1, 0); //当计算i时用到上一层的j-v[i][k]的值,所以需要倒着遍历
for(int i = 1; i <= n; ++i){
for(int j = m; j >= 0; --j){
for(int k = 0; k <= s[i]; ++k)
if( j >= v[i][k] )
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
}
}
cout<<f[m]<<endl;
return 0;
}
分组背包:集合是 前i个物品中选一个,且体积不超过j的所有选法。取最大值。 与多重背包不同的是,这里选择哪一个,因而0-k 必须全部遍历。 多重背包选择前k个,在超过背包体积j时即可停止