题目描述

有 $N$ 种物品和一个容量是 $V$ 的背包。

第 $i$ 种物品最多有 $s_i$ 件,每件体积是 $v_i$,价值是 $w_i$。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数 $N,V$,用空格隔开,分别表示物品种数和背包容积。

接下来有 $N$ 行,每行三个整数 $v_i,w_i,s_i$,用空格隔开,分别表示第 $i$ 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

$0 \lt N, V \le 100$
$0 \lt v_i, w_i, s_i \le 100$

输入样例

1
2
3
4
5
4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

1
10

题目求解

方式一 三重for循环求解

相比$01$背包问题增加一层循环对物品的多种可选方式$s_i$进行遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 110;

int n, m, f[N][N], v, w, s;

int main() {
cin >> n >> m;
for(int i = 1; i <= n ;i ++) {
cin >> v >> w >> s;
for(int j = 0 ; j <= m ;j ++) {
f[i][j] = f[i - 1][j];
for(int k = 0; k <= s && j >= k * v; k ++) {
f[i][j] = max(f[i][j] , f[i - 1][j - k * v] + k * w);
}
}
}
cout << f[n][m];
}

滚动数组的优化,将二维数组优化为一维数组,相当于代码的等价变形,时间复杂度$O(n \times m \times s)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 110;

int n, m, f[N], v, w, s;

int main() {
cin >> n >> m;
for(int i = 1; i <= n ;i ++) {
cin >> v >> w >> s;
for(int j = m ; j >= 0 ;j --) {
for(int k = 1; k <= s && j >= k * v; k ++) {
f[j] = max(f[j] , f[j - k * v] + k * w);
}
}
}
cout << f[m] << endl;
}

方式二 转换为01背包问题求解

将所有的商品包括重复的商品当作一个商品来看,这就转换成了$01$背包问题,时间复杂度$O(n \times m \times s)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 110;
const int M = 10010;

int n, m, f[N], v, w, s, V[M], W[M], t;

int main() {
cin >> n >> m;
while(n --) {
cin >> v >> w >> s;
while(s --) {
V[++t] = v;
W[t] = w;
}

}
for(int i = 1; i <= t; i ++)
for(int j = m; j >= V[i] ; j --)
f[j] = max(f[j], f[j - V[i]] + W[i]);
cout << f[m] << endl;
return 0;
}