0/1背包
给定 N 件物品,第i个物品的重量为 weight[i],价值为value[i]。现挑选物品放入背包中,假定背包能承受的最大重量为 V,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
- “已经处理的物品数” 作为DP的阶段,
- 以“背包中已经放入的物品总体积”作为附加维度
F[i, j] 表示从前i个物品中选出了总体积为j的物品放入背包
F [ i , j ] = max { F [ i − 1 , j ] 不 选 用 第 i 个 物 品 F [ i − 1 , j − v a l u e [ i ] ] + w e i g h t [ i ] i f j > = w e i g h t [ i ] 选 用 第 i 个 物 品 F[i,j]=\max\left\{ \begin{array}{l} F[i-1,j] ~~~~~~~~~不选用第i个物品\\ F[i-1,j-value[i]]+weight[i]~~~ if~j>= weight[i] ~选用第i个物品\\ \end{array} \right. F[i,j]=max{F[i−1,j] 不选用第i个物品F[i−1,j−value[i]]+weight[i] if j>=weight[i] 选用第i个物品初值:F[0,0] = 0,其余均为负无穷,目标max{F[N, j]} 0<=j<=V
https://www.cnblogs.com/kkbill/p/12081172.html
https://labuladong.github.io/ebook/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%B3%BB%E5%88%97/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98.html
// 时间O(N*V) 空间O(N*V)
public int maxValue1(int[] weight, int[] value, int V) {
int N = weight.length;
if (N == 0) return 0;
int[][] dp = new int[N+1][V+1];
//dp[i][w] 的定义如下:
// 对于前 i 个物品,当前背包的容量为 w,这种情况下可以装的最大价值是 dp[i][w]
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= V; j++) {
if (j - weight[i-1] < 0) {
// 这种情况下只能选择不装入背包
dp[i][j] = dp[i-1][j];
} else {
// 装入或不装入,择优
dp[i][j] = Math.max(dp[i-1][j-weight[i-1]] + value[i-1],
dp[i-1][j]);
// 使用的是i-1阶段的j-weight[i-1],j
}
}
}
return dp[N][V];
}
// 时间O(N*V) 空间O(V)
public int maxValue2(int[] weight, int[] value, int V) {
int N = weight.length;
int[] dp = new int[V+1];
for (int i = 1; i <= N; i++) {
for (int j = V; j >= 1; j--) { //
if (j - weight[i-1] < 0) {
dp[j] = dp[j];
} else {
dp[j] = Math.max(dp[j],
dp[j-weight[i-1]] + value[i-1]);
}
}
}
return dp[V];
}
// 时间O(N*V) 空间O(V)
public int maxValue3(int[] weight, int[] value, int V) {
int N = weight.length;
int[] dp = new int[V+1];
for (int i = 1; i <= N; i++) {
for (int j = V; j >= weight[i-1]; j--) { // 倒序
dp[j] = Math.max(dp[j], dp[j-weight[i-1]] + value[i-1]);
// 如果正序的话,使用的j-weight[i-1],就是i阶段的而不是i-1阶段的了
}
}
return dp[V];
}
完全背包
给定 N 件物品,第i个物品的重量为 weight[i],价值为value[i],并且有无数个。现挑选物品放入背包中,假定背包能承受的最大重量为 V,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
F[i,j]表示从前i种物品中选出了总质量为j的物品放入背包,物品的最大价值和。
F [ i , j ] = max { F [ i − 1 , j ] 尚 未 选 过 第 i 种 物 品 F [ i − 1 , j − v a l u e [ i ] ] + w e i g h t [ i ] i f j > = w e i g h t [ i ] 从 第 i 种 物 品 中 选 一 个 F[i,j]=\max\left\{ \begin{array}{l} F[i-1,j] ~~~~~~~~~尚未选过第i种物品\\ F[i-1,j-value[i]]+weight[i]~~~ if~j>= weight[i] ~从第i种物品中选一个\\ \end{array} \right. F[i,j]=max{F[i−1,j] 尚未选过第i种物品F[i−1,j−value[i]]+weight[i] if j>=weight[i] 从第i种物品中选一个初值:F[0,0] = 0,其余均为负无穷,目标max{F[N, j]} 0<=j<=V
for i ← 1 to N
for v ← Ci to V
F[i, v] ← max{F[i − 1, v], F[i − 1, v − Ci] + Wi}
def CompletePack(F, C, W)
for v ← C to V
F[v] ← max{F[v], f[v − C] + W}
多重背包
给定 N 件物品,第i个物品的重量为 weight[i],价值为value[i],并且有nums[i]个。现挑选物品放入背包中,假定背包能承受的最大重量为 V,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
因为对于第 i 种物品有 nums[i]+ 1 种策略:取 0 件,取 1 件……取 nums[i]件。
令 F[i, v] 表示前 i 种物品恰放入一个容量为 v 的背包的最大价值,
则有状态转移方程:
F [ i , v ] = max { F [ i − 1 , v − k ∗ w e i g h t [ i ] ] + k ∗ v a l u e [ i ] ∣ 0 ≤ k ≤ M i } . F[i,v]=\max\left\{ F[i − 1, v − k ∗ weight[i]] + k ∗ value[i]~~~~~~| 0 ≤ k ≤ Mi\right\}. F[i,v]=max{F[i−1,v−k∗weight[i]]+k∗value[i] ∣0≤k≤Mi}.复杂度是 O(V Σnums[i])。
分组背包
给定 N 件物品,第i组有个nums[i]个物品。第i组的第j个物品的重量为 weight[i][j],价值为value[i][j]。现挑选物品放入背包中,假定背包能承受的最大重量为 V,要求选择若干个物品放入背包,使得每组之多选择一个物品并且物品总质量不超过V的前提下,使得装入背包中物品的总价值最大?
F [ i , j ] = max { F [ i − 1 , j ] 不 选 第 i 组 物 品 max 1 < = k < = n u m s [ i ] F [ i − 1 , j − w e i g h t [ i ] [ k ] ] + v a l u e [ i ] [ k ] i f j > = w e i g h t [ i ] 选 第 组 的 某 个 物 品 k F[i,j]=\max\left\{ \begin{array}{l} F[i-1,j] ~~~~~~~~~不选第i组物品\\ \max_{1<=k<=nums[i]}F[i-1,j-weight[i][k]]+value[i][k]~~~ if~j>= weight[i] ~选第组的某个物品k\\ \end{array} \right. F[i,j]=max{F[i−1,j] 不选第i组物品max1<=k<=nums[i]F[i−1,j−weight[i][k]]+value[i][k] if j>=weight[i] 选第组的某个物品k
和前几个背包模型一样,我们可以省略掉F数组的第一维,用j的倒叙循环来空控制“阶段i”的状态只能从“阶段i-1”转移而来。
for (int i = 1; i <= N; i++)
for (int j = V; j >= 0; j--)
for (int k = 1; k <= nums[i]; k++)
if (j >= weight[i][k])
dp[j] = Math.max(dp[j], dp[j-weight[i][k]] + value[i][k];
- 除了倒序循环j之外,对于每一组内nums[i]个物品的循环k应该放在j的内层。
- 从背包的角度看,这是因为每组内之多选择一个物品,若把k置于j的外层,就会类似于多重背包,每组物品在F数组上的转移会产生积累,最终可以选择超过1个物品。
- 从动态规划的角度,i是“阶段”,i和j共同构成“状态”,而k是“决策”----在第i组内使用哪一个物品,这三者的顺序绝对不能混淆。