混合背包
有 N 种物品和一个容量是 V 的背包。
物品一共有三类:
- 第一类物品只能用1次(01背包);
- 第二类物品可以用无限次(完全背包);
- 第三类物品最多只能用 si 次(多重背包);
每种体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
分类讨论
// Problem: 混合背包问题
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/7/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// Code by: ING__
//
// Edited on 2021-07-27 21:53:47
#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
int f[N];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
int v, w, s;
cin >> v >> w >> s;
if(s == 0){
for(int j = v; j <= m; j++){
f[j] = max(f[j], f[j - v] + w);
}
}
else{
if(s == -1){ // 看成特殊的多重背包
s = 1;
}
// 多重背包二进制优化
for(int k = 1; k <= s; k *= 2){
for(int j = m; j >= k * v; j--){
f[j] = max(f[j], f[j - k * v] + w * k);
}
s -= k;
}
if(s){
for(int j = m; j >= s * v; j--){
f[j] = max(f[j], f[j - s * v] + w * s);
}
}
}
}
cout << f[m];
return 0;
}
有依赖的背包
有 N 个物品和一个容量是 V 的背包。
物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
状态表示
所有从以u为根的子树中选,且总体积不超过 j 的方案
属性
MAX
状态计算
dfs在遍历到 x 结点时,先考虑一定选上根节点 x ,因此初始化 f[x][v[x] ~ m] = w[x]
在分组背包部分:
j 的范围 [ m , v[x] ] 小于v[x]则没有意义因为连根结点都放不下;
k 的范围 [ 0 , j-v[x] ],当大于j-v[x]时分给该子树的容量过多,剩余的容量连根节点的物品都放不下了;
Code
#include <iostream>
#include <vector>
using namespace std;
const int N = 110;
int n, m;
vector<int> g[N]; // 邻接矩阵存图
int f[N][N]; // 以u为根节点,容量不超过j的最大价值
int v[N];
int w[N];
void dfs(int u){
for(int i = v[u]; i <= m; i++) f[u][i] = w[u];
for(int i = 0; i < g[u].size(); i++){
int y = g[u][i];
dfs(y);
for(int j = m; j >= v[u]; j--){
for(int k = 0; k <= j - v[u]; k++){
f[u][j] = max(f[u][j], f[u][j - k] + f[y][k]);
}
}
}
}
int main(){
cin >> n >> m;
int root = 1;
for(int i = 1; i <= n; i++){
int p;
cin >> v[i] >> w[i] >> p;
if(p == - 1) root = i;
else g[p].push_back(i);
}
dfs(root);
cout << f[root][m];
return 0;
}
状态联系
体积最多是 j;体积恰好是 j;体积至少是 j
求价值(价值最值)初始化
这里本质上都进行了降维优化,所以初始化的时候本质上是对f[0][j]进行初始化
-
体积最多是 j 的时候,初始化全部为0,保证枚举体积 v 大于等于0
-
因为初始化为0即根据状态表示,我们每一个方案都是在枚举过程中,如果不超过这个体积是有这个状态的合法性的,即就是选不了,最后价值是0,也满足了不超过体积 j 。
(只会求价值的最大值)
-
-
体积恰好是 j 的时候,f[0] = 0, f[i] = |INF|,保证枚举体积 v 大于等于0
- 当f[0]的时候,即一个都不选,体积为0,显然这个时候价值就是0;当别的状态的时候,最初始的状态一定达不到恰好这个体积下有合法的价值,所以为了不选这个状态,即确定其不合法性,我们根据要求的是最大还是最小来确定其INF的正负性
-
体积至少是 j 的时候,f[0] = 0, f[i] = INF
-
这里的表述也有上面类似,但是这里可以不用保证体积要大于等于0,因为就像上面提到的潜水员那个题。如果体积是负数也符合至少这个条件,因为每个体积都大于0,所以如果这个状态为负数的情况下,也等价于体积为0的情况。
而这INF的含义也是当我还没选的时候,显然这时候方案也是不合法的。即体积至少为 j ,但一个都没选,显然是不合法的
(只会求价值的最大值)
-
无穷的目的是在更新的时候不使用这个值,正负取决于定义要求的是最大还是最小值
求方案数的初始化
二维情况
- 体积至多j,f[0][i] = 1, 0 <= i <= m,其余是0
- 体积恰好j,f[0][0] = 1, 其余是0
- 体积至少j,f[0][0] = 1,其余是0
一维情况
- 体积至多j,f[i] = 1, 0 <= i <= m,
- 体积恰好j,f[0] = 1, 其余是0
- 体积至少j,f[0] = 1,其余是0