01.0-1背包
了解01背包
有 n 件物品和一个最大承重为 W 的背包,每件物品的重量是 wi 、价值是 vi,在保证总重量不超过 W 的前提下,选择某些物品装入背包,背包的最大总价值是多少?每个物品只有 1 件,也就是每个物品只能选择 0 件或者 1 件。( values 是价值数组,weights 是重量数组)
状态转移方程
定义状态:dp(i, j) 是最大承重为 j、有前 i 件物品可选时的最大总价值
dp(i, j) = max { dp(i – 1, j), dp(i – 1, j – weights[i – 1]) + values[i – 1] }
伪代码
for (int i = 1; i <= n; i++) //n件物品 i表示前i个物品
for (int j = 1; j <= w; j++) //最大承重为 W
if(w[i]>j) dp[i][j]=dp[i-1][j];
else dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
状态转移方程压缩
dp(j) = max { dp(j), dp(j – weights[i – 1]) + values[i – 1] } 注意(要逆向推导,具体如下)
for (int i = 1; i <=n; i++) {//n件物品 i表示前i个物品
for (int j = w; j >=w[i]; j--) {//当j < w[i],证明第i件物品不能选(必须要逆向推)
dp[j]=Math.max(dp[j], dp[j-w[i]]+v[i]);
}
}
练习与题解
- P1049 [NOIP2001 普及组 装箱问题:https://www.luogu.com.cn/problem/P1049
Java题解:https://blog.csdn.net/qq_46237746/article/details/121682567 - P2370 yyy2015c01 的 U 盘:https://www.luogu.com.cn/problem/P2370
Java题解:https://blog.csdn.net/qq_46237746/article/details/123687777
02.完全背包
了解完全背包
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。第 i 种物品的重量是 w[i] 、价值是 v[i]。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
基于0-1背包的完全背包
for (int i = 1; i <=N; i++) {//有 N 种物品 i表示前i种物品
for (int j = V; j >=w[i]; j--) {//从右到左进行覆盖,当j < weights[i],证明第i件物品不能选
for (int k = 0; k <= j/w[i]; k++) {
dp[j]=Math.max(dp[j], dp[j-k*w[i]]+k*v[i]);
}
}
}
完全背包状态转态移方程
定义状态:dp(i, j) 是最大承重为 j、有前 i 件物品可选时的最大总价值
则其状态转移方程为:dp[i][j]=Math.max( dp[i-1][j], dp[i][j-w[i]]+v[i] )
状态压缩
dp(j) = max { dp(j), dp(j – w[i]) + v[i] } 注意(要从小到大递推)
具体如下:
for (int i = 1; i <= N; i++)
for (int j = w[i]; j <= V; j++)
dp[j]=Math.max(dp[j], dp[j-w[i]]+v[i]);
练习与题解
- P1853 投资的最大效益:https://www.luogu.com.cn/problem/P1853
Java题解:https://blog.csdn.net/qq_46237746/article/details/123909002 - P5662 [CSP-J2019] 纪念品:https://www.luogu.com.cn/problem/P5662
Java题解:https://blog.csdn.net/qq_46237746/article/details/123561594
03.多重背包
了解多重背包
有 N 种物品和一个容量是 V 的背包,每种物品都有n[i]可用。第 i 种物品的重量是 wi 、价值是 vi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
状态转移方程
for (int i = 1; i <= n; i++) //有N种物品
for (int j = v; j>=0; j--) //容量v
for (int k = 1; k <= n[i]; k++) //第i个物品的数量为n[i]
if(j>=k*w[i])
dp[j]=Math.max(dp[j], dp[j-k*w[i]]+k*v[i]);
或
for (int i = 1; i <= n; i++) //有N种物品
for (int k = 1; k <= n[i]; k++) //第i个物品的数量为n[i]
for (int j = v; j >= w[i]; j--) //容量v
dp[j]=Math.max(dp[j], dp[j-w[i]]+v[i]);
练习与题解
- P2347 [NOIP1996 提高组] 砝码称重:https://www.luogu.com.cn/problem/P2347
Java题解:https://blog.csdn.net/qq_46237746/article/details/123854837 - P6771 [USACO05MAR]Space Elevator 太空电梯:https://www.luogu.com.cn/problem/P6771
Java题解:https://blog.csdn.net/qq_46237746/article/details/123855331
04.二维费用背包
了解二维费用背包
有n件物品,两种不同的费用,选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为a[i]和 b[i]。两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为w[i]。
状态转移方程
定义状态:dp[k][i][j]
背包两种容量分别为i,j的情况下,前k件物品可选择时物品的最大价值
dp[k][i][j]=Math.max(dp[k-1][i][j], dp[k-1][i-a[k]][j-b[k]]+w[k]) //a[k]<=i && b[k]<=j
dp[k][i][j]=dp[k-1][i][j]; //不满足条件:a[k]<=i && b[k]<=j
具体案例
for (int k = 1; k <= n; k++) //有n件物品
for (int i = 1; i <= V; i++) //背包最大容量V
for (int j = 1; j <= U; j++) { //背包最大容量U
if(i>=a[k]&&j>=b[k]) {
dp[k][i][j]=Math.max(dp[k-1][i][j], dp[k-1][i-a[k][j-b[k]+1);
}else {
dp[k][i][j]=dp[k-1][i][j];
}
}
状态压缩
dp[k][i][j]=Math.max(dp[k-1][i][j], dp[k-1][i-a[k][j-b[k]+w[k]) //逆向推导(从大到小)
具体案例
for (int k = 1; k <= n; k++)
for (int i = V; i >= a[k]; i--)
for (int j = U; j >= b[k]; j--)
dp[i][j] = dp[i - a[k][j - b[k]] + w[k];
练习与题解
- 榨取kkksc03):https://www.luogu.com.cn/problem/P1855
- P1509 找啊找啊找GF:https://www.luogu.com.cn/problem/P1509
Java题解:https://blog.csdn.net/qq_46237746/article/details/123776023
05.分组背包问题
了解分组背包
分组背包:有groupNum组物品,N个物品,一个容量是capacity的背包。每组物品有若干个,同一组内的物品最多只能选一个。
状态转移方程
定义状态: dp[k][j] 最大承重为j,有前k组物品可选时的最大价值
for (int k = 1; k <= groupNum; k++) // 所属组k
for (int j = 0; j <= capacity; j++) // 容量j
for (int i = 0; i < I; i++) // 所属分组k的物品I
dp[k][j]=Math.max(dp[k - 1][j],
dp[k - 1][j - 物品i的重量] + 物品i的价值);
状态转移方程压缩
定义状态:dp[j] 最大容量为j的前提下,物品的最大价值
for (int k = 1; k <= groupNum; k++) // 所属组k
for (int j = capacity; j >= 0; j--) // 容量j
for (int i = 0; i < I; i++) // 所属分组k的物品I
dp[j]=Math.max(dp[j], dp[j-物品i的重量] + 物品i的价值);
练习与题解
- P1757通天之分组背包:https://www.luogu.com.cn/problem/P1757
Java题解:https://blog.csdn.net/qq_46237746/article/details/121941066 - P5322 [BJOI2019] 排兵布阵:https://blog.csdn.net/qq_46237746/article/details/123850976
Java题解:https://blog.csdn.net/qq_46237746/article/details/123850976
06.混合背包
前面几种背包混合
案例:https://www.luogu.com.cn/problem/P1833
07.有依赖的背包/树背包
https://oi-wiki.org/dp/tree/#_3
08.补充
PS:参考资料
- 洛谷刷题题单:https://www.luogu.com.cn/training/8917
- (yyds)https://oi-wiki.org/dp/knapsack/