main
黄海 1 year ago
parent 110d4f8a8f
commit 5d2b4f9721

@ -131,7 +131,7 @@ int main() {
$A:$我们可以将结论推广到不同属性的情况下,本题的属性是数量,但如果是最大价值呢?
我们不难得到需要将$f[0]$初始化为$0$$f[1\sim n]$初始化为负无穷
为什么要这样设置呢?因为每一个新状态,都需要知道它可以从哪些旧状态转移而来,如果上一个状态是合法的,那么有可能从上一个状态转移而来,但如何标识上一个状态是不是合法呢?比如如果初始化状态值是$0$,并且上一个状态是$0$,表示的是目前的最大值,那是不合法呢?不好说啊,为什么呢?
为什么要这样设置呢?因为每一个新状态,都需要知道它可以从哪些旧状态转移而来,如果上一个状态是合法的,那么有可能从上一个状态转移而来,但如何标识上一个状态是不是合法呢?比如如果初始化状态值是$0$,并且上一个状态是$0$,表示的是目前的最大值,那是不合法呢?不好说啊,为什么呢?
* 上一个状态不合法,没有状态转移过来
* 上一个状态合法,因为有负数等原因,造成最大值确实为$0$

@ -109,4 +109,66 @@ int main() {
**总结**
- $01$背包,还是背一维的形式比较好,一来代码更短,二来空间更省,倒序就完了。
- 二维费用的$01$背包,简化版本的$01$背包模板就有了用武之地,因为三维数组可能会爆内存。
- 二维费用的$01$背包,简化版本的$01$背包模板就有了用武之地,因为三维数组可能会爆内存。
### 三、$01$背包之恰好装满
**[$AcWing$ $278$. 数字组合](https://www.acwing.com/problem/content/280/)**
**二维代码**
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const int M = 10010;
int n, m;
int v;
int f[N][M];
int main() {
cin >> n >> m;
for (int i = 0; i <= n; i++) f[i][0] = 1; // base case
for (int i = 1; i <= n; i++) {
cin >> v;
for (int j = 1; j <= m; j++) {
// 从前i-1个物品中选择装满j这么大的空间假设方案数是5个
// 那么在前i个物品中选择装满j这么大的空间方案数最少也是5个
// 如果第i个物品可以选择那么可能使得最终的选择方案数增加
f[i][j] = f[i - 1][j];
// 增加多少呢前序依赖是f[i - 1][j - v]
if (j >= v) f[i][j] += f[i - 1][j - v];
}
}
// 输出结果
printf("%d\n", f[n][m]);
return 0;
}
```
**一维代码**
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 10010;
int n, m;
int v;
int f[N]; // 在前i个物品体积是j的情况下恰好装满的方案数
int main() {
cin >> n >> m;
// 体积恰好j, f[0]=1, 其余是0
f[0] = 1;
for (int i = 1; i <= n; i++) {
cin >> v;
for (int j = m; j >= v; j--)
f[j] += f[j - v];
}
printf("%d\n", f[m]);
return 0;
}
```

Loading…
Cancel
Save