You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
3.7 KiB

2 years ago
##[$AcWing$ $9$. 分组背包问题](https://www.acwing.com/problem/content/description/9/)
### 一、题目描述
有 $N$ 组物品和一个容量是 $V$ 的背包。
1 year ago
每组物品有若干个,**同一组内的物品最多只能选一个**。
2 years ago
每件物品的体积是 $v_{ij}$,价值是 $w_{ij}$,其中 $i$ 是组号,$j$ 是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
**输入格式**
第一行有两个整数 $NV$,用空格隔开,分别表示物品组数和背包容量。
接下来有 $N$ 组数据:
每组数据第一行有一个整数 $S_i$,表示第 $i$ 个物品组的物品数量;
每组数据接下来有 $S_i$ 行,每行有两个整数 $v_{ij},w_{ij}$,用空格隔开,分别表示第 $i$ 个物品组的第 $j$ 个物品的体积和价值;
**输出格式**
输出一个整数,表示最大价值。
**数据范围**
$0<N,V100$
$0<S_i100$
$0<v_{ij},w_{ij}100$
**输入样例**
```cpp {.line-numbers}
3 5
2
1 2
2 4
1
3 4
1
4 5
```
**输出样例**
```cpp {.line-numbers}
8
```
### 二、解题思路
直接上 **分组背包** 的 **闫氏$DP$分析法**
<center><img src='https://cdn.acwing.com/media/article/image/2021/06/21/55909_0a17f1d9d2-IMG_40E676992253-1.jpeg'></center>
初始状态 $f[0][0]$
目标状态 $f[N][M]$
#### 状态表示
$f[i][j]$ 从前 $i$ 组物品中选择且总体积不大于 $j$ 的最大价值
#### 状态转移
1 year ago
针对第 $i$ 组物品,将整个状态划分成 $s[i]+1$ 类:
* ① 第$i$组物品一个都不要:$f[i][j] = f[i-1][j]$
* ② 选第 $i$ 组物品的第一个物品:$f[i][j] = f[i-1][j-v_1]+w_1$
* ③ 选第 $i$ 组物品的第二个物品:$f[i][j] = f[i-1][j-v_2]+w_2$
* ④ 选第 $i$ 组物品的第 $k$ 个物品:$f[i][j] = f[i-1][j-v_k]+w_k$
* ...
2 years ago
1 year ago
$$\large f[i][j]=max(f[i][j], f[i-1][j-v_k]+w_k)) \ k \in [0, 1, 2,...,s_i]$$
2 years ago
1 year ago
#### 初始化
$f[0][0\sim m] = 0$ 表示在选择 `0` 组物品时对于任何体积来讲,其最大价值均为 `0`
2 years ago
### 三、二维数组版本
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;
int f[N][N], v[N][N], w[N][N], s[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> s[i]; // 第i个分组中物品个数
for (int j = 1; j <= s[i]; j++)
cin >> v[i][j] >> w[i][j]; // 第i个分组中物品的体积和价值
}
1 year ago
for (int i = 1; i <= n; i++) // 每组
for (int j = 0; j <= m; j++) { // 每个合法体积
f[i][j] = f[i - 1][j]; // 如果一个都不要那么这一组就相当于白费给你机会也不中用继承于i-1
for (int k = 1; k <= s[i]; k++) // 选择第k个
2 years ago
if (j >= v[i][k])
f[i][j] = max(f[i][j], f[i - 1][j - v[i][k]] + w[i][k]); // 枚举每一个PK一下大小
}
// 输出打表结果
printf("%d", f[n][m]);
return 0;
}
1 year ago
2 years ago
```
### 四、一维数组版本
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;
int v[N][N], w[N][N], s[N];
int f[N];
int main() {
cin >> n >> m;
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 (j >= v[i][k])
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
printf("%d\n", f[m]);
return 0;
}
```