'commit'
This commit is contained in:
136
TangDou/AcWing/BeiBao/9.md
Normal file
136
TangDou/AcWing/BeiBao/9.md
Normal file
@@ -0,0 +1,136 @@
|
||||
##[$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;
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user