136 lines
3.7 KiB
Markdown
136 lines
3.7 KiB
Markdown
##[$AcWing$ $9$. 分组背包问题](https://www.acwing.com/problem/content/description/9/)
|
||
|
||
### 一、题目描述
|
||
有 $N$ 组物品和一个容量是 $V$ 的背包。
|
||
|
||
每组物品有若干个,**同一组内的物品最多只能选一个**。
|
||
|
||
每件物品的体积是 $v_{ij}$,价值是 $w_{ij}$,其中 $i$ 是组号,$j$ 是组内编号。
|
||
|
||
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
|
||
|
||
输出最大价值。
|
||
|
||
**输入格式**
|
||
第一行有两个整数 $N,V$,用空格隔开,分别表示物品组数和背包容量。
|
||
|
||
接下来有 $N$ 组数据:
|
||
|
||
每组数据第一行有一个整数 $S_i$,表示第 $i$ 个物品组的物品数量;
|
||
每组数据接下来有 $S_i$ 行,每行有两个整数 $v_{ij},w_{ij}$,用空格隔开,分别表示第 $i$ 个物品组的第 $j$ 个物品的体积和价值;
|
||
|
||
**输出格式**
|
||
输出一个整数,表示最大价值。
|
||
|
||
**数据范围**
|
||
$0<N,V≤100$
|
||
|
||
$0<S_i≤100$
|
||
|
||
$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$ 的最大价值
|
||
|
||
#### 状态转移
|
||
针对第 $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$
|
||
* ...
|
||
|
||
$$\large f[i][j]=max(f[i][j], f[i-1][j-v_k]+w_k)) \ k \in [0, 1, 2,...,s_i]$$
|
||
|
||
#### 初始化
|
||
$f[0][0\sim m] = 0$ 表示在选择 `0` 组物品时对于任何体积来讲,其最大价值均为 `0`
|
||
|
||
|
||
### 三、二维数组版本
|
||
```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个分组中物品的体积和价值
|
||
}
|
||
|
||
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个
|
||
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;
|
||
}
|
||
|
||
```
|
||
|
||
### 四、一维数组版本
|
||
```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;
|
||
}
|
||
``` |