1. 01 背包问题
分析 :
1. 二维动态规划:
f[i][j] = v
-> 用前 i 件物品,使用容量为 j 的情况的 能获得的最大价值
转移方程:
对于 物品 i 来说
有取和不取两种方案:
然后两者取较大值即可
(a). 取:
f[i][j] = f[i-1][j-wi] + vi;
(b). 不取:
f[i][j] = f[i-1][j]
初始化:
f[0][0] = 0;
答案:
max{ f[i][] }
优化:
从上面的二维动态规划可以看出来,f[i] 之和 f[i-1] 的状态有关
所以可以用滚动数组的方式来优化空间复杂度
2. 一维动态规划
和上述的思路类似,对于遍历到的每一个 i 物品 ,f[j] 表示在使用 [0:i] 物品的情况下,容量限制 为 j 的情况下所能获得的最大价值
相当于在计算 f[i][j] = f[i-1][j - wi] + vi 要转变为 f[j] = f[j - wi] + vi
相当对在计算 f[j] 的时候比 f[j] 小的应该还未计算,所以遍历的时候从容量大到小遍历即可
***
此处不需要计算 max{f[]}
f[m] 就是答案
因为此时是全部的 f[i] 都被初始化为 0
设 能获得的最大价值 使用容量是 k
相当于从 f[0] 加入这些值 转移到了 f[k] = max_v
也相当于可以从 f[m-k] 加入这些值 转移到了 f[m] = max_v
***
2. 完全背包
和 01 问题的区别:
每一件物品可以选择 无限件
一维做法:
f[i] -> 总体积为 i 的情况下,能获得的最大价值
res = { f[] }
正常情况下:
按照 01 背包的思路:
for(int i = 1;i <= n;i++) {
// 对于物品的遍历
for(int j = m;j >= w[i];j--) {
// 对于可用体积的遍历
for(int k = 1;k*w[i] >= j;k++) {
// 此时放入多少个 物品 i
f[j] = max(f[j],f[j - k * w[i]] + k * v[i]);
// f[j - k * w[i]] 都是没有计算过的,上层的计算结果
// 所以不会影响到该层计算
}
}
}
但是也可以不用 k 的那层循环,只需要改变 j 的遍历
for(int i = 1;i <= n;i++) {
// 对于物品的遍历
for(int j = w[i];j <= m;j++) {
f[j] = max(f[j],f[j - w[i]] + v[i]);
// 因为 我们此时不用关心 f[j - w[i]] 是否和物品i有关
// 因为 物品 i 是可以无限制加的, 就算 f[j - w[i]] 中已有了 物品 i 也没有关系
}
}
// 此时对于答案也就是 f[m] 同理于 上面 01 背包的情况
3. 多重背包问题
相比较于上述的完全背包:
多了每件物品的最多使用数量
解法 1:
使用三重循环即可
第一个循环物品
第二个循环当前体积
第三个循环每件物品的个数
限制 : 当 n m s <= 100 时,否则会超时
解法 2:
二进制优化方法
当 n m s <= 2000 时
用上述的方法就会超时了
所以可以用二进制的方法来计算 :
比如某物品 i 的个数 s = 10
那么可以将物品 i 拆分成 4 份 每份分别有 1,2,4,3 件该物品 i
这样就 退化为 01 背包问题 了 对于物品 i 的选取就变成了 4 份的 选与不选的问题,且相互独立
时间复杂度 也就变成了 : O(n * m * log(s) ) = O(2 * 10 ^ 7)
解法3:
单调队列优化 (*难度较大 暂省略*)
主要思路 :在三重循环中的 第三重循环 (对 物品 i 的个数 ) 用单调队列来优化
4. 混合背包问题
思路 :就是对于每次放入的物品 i 进行判断 :
1. 对于 01 背包 的 物品 :
直接按照 01 背包 的遍历来进行 从大到小遍历
2. 对于 完全背包 的 物品 :
直接按照 完全背包 的遍历来进行 从小到大遍历
3. 对于有个数限制 的 物品 :
按照二进制优化来分成 ceil(log(s)) 份 来进行
5. 二维费用的背包问题
思路 :
f[i][j] -> 体积为 i,重量为 j 的情况下最大的价值是多少
每个物品只能用一次,则 从大到小 遍历即可
6. 分组背包问题 :
思路:
遍历每组 和其中的物品
7. 背包问题求方案数
按照 01 背包 的思路:
f[i] = x; 体积恰好为 i 的情况下 能获得的最大价值为 x
再开一个 g[N] 来表示 :
g[i] 表示 : f[i] = x 的情况下 方案数 的大小
1. 来判断 对于当前的体积 判断 当前体积的最大价值是从上面的哪个方案转移来的
2,3. s 用于记录 当前体积的最大价值的方案数
若是从 f[j] 来的就加上 g[j]
若是从 f[j - v] 来的就加上 g[j - v]
最后更新下 g[j] 即可
4. 首先一次遍历来求最后的能获得的最大的价值 maxw
然后计算能得到最大价值的方案数
8. 求背包问题的方案
用一个二维数组来标记每一轮选择的转移结果
箭头处 :
当 f[n][m] == f[n-1][m] 说明 第n件物品是不选择的
当 f[n][m] == f[n-1][m - vn] + wn 说明 第n件物品是选择了
9. 有依赖的背包问题
暂略,后面遇到再补充