视频链接:背包九讲专题_哔哩哔哩_bilibili
一,01 背包问题
题目链接:2. 01背包问题 - AcWing题库
题解:
二维代码:
f [ i ][ j ] 表示只考虑到前 i 个物品,且总体积恰小于等于 j 的情况下,总价值最大是多少
递推式:
情况①:不选第 i 个物品,f [ i ][ j ] = f [ i - 1 ][ j ]
情况②:选第 i 个物品,f [ i ][ j ] = f [ i - 1 ][ j - v[i] ] + w[ i ]
f [ i ][ j ] = max(①,②)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 1010 int n, m; // 物品数,背包体积 int f[N][N]; int v[N], w[N]; // 物品体积,物品价值 int max(int a, int b) { return a > b ? a : b; } int main(void) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d%d", &v[i], &w[i]); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { f[i][j] = f[i - 1][j]; // 默认不选 if (j >= v[i]) // 如果可以选,即背包体积大于物品体积 f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]); } } printf("%d ", f[n][m]); system("pause"); return 0; }
解析:
f[i][j] = f[i - 1][j]; // 默认不选 if (j >= v[i]) // 如果背包体积大于物品体积 f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
上面这段代码就是从第 i-1 个物品的状态,推导第 i 个物品的状态的递推式。相较于上文 “选于不选” 的判断条件,这里省略了 “不选“ 的判断条件,并在可以选择的时候,比较选与不选各自的价值。也可以理解为:不能选的话,只能不选;可以选的话,就要看选与不选哪个价值大咯。
二维压缩为一维:01 背包的空间优化
f [ j ] 表示 如果当前考虑的是第 i 个物品,那么在总体积小于等于 j 的情况下,只考虑到前 i - 1 个物品的总价值最大是多少
递推式:从第 i-1 个物品的状态,推导出第 i 个物品的状态
情况①:不选第 i 个物品,f [ j ] = f [ j ] ,(注意,虽然这里等式左右一样,但意义不一样,等式左边代表的是只考虑到前 i 个物品,而等式右边代表的是只考虑到前 i - 1 个物品。下文会将这种从 只考虑到前 i - 1 个物品 到 只考虑到前 i 个物品 的变化叫刷新。这也是为什么第二层循环要从后往前循环的原因 )
情况②:选第 i 个物品,f [ j ] = f[ j - v[i] ] + w[ i ],(注意,f [ j ] 代表的是只考虑到前 i 个物品, f[ j - v[i] ] 代表的是只考虑到前 i - 1 个物品。下文会将这种从 只考虑到前 i - 1 个物品 到 只考虑到前 i 个物品 的变化叫刷新。这也是为什么第二层循环要从后往前循环的原因 )
f [ i ][ j ] = max(①,②)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 1010 int n, m; // 物品数,背包体积 int f[N]; int v[N], w[N]; // 物品体积,物品价值 int max(int a, int b) { return a > b ? a : b; } int main(void) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d%d", &v[i], &w[i]); for (int i = 1; i <= n; i++) for (int j = m; j >= v[i]; j--) f[j] = max(f[j], f[j - v[i]] + w[i]); printf("%d ", f[m]); system("pause"); return 0; }
解析:
f[j] = max(f[j], f[j - v[i]] + w[i]);
上面这段代码就是从第 i-1 个物品的状态,推导第 i 个物品的状态的递推式。因为不选是 f[ j ] = f[ j ],没有变化,所以虽然这里没有表示出不选的代码,但实际上还是有这个含义的。再来说一下不选,f[ j - v[ i] ] + w[ i ],问题就出在这个 f[ j - v[ i] ] 身上,因为如果我们 j 是正常的从小到大递增的话,那么 f[ j ] 肯定 还没经过刷新,但 f[ j - v[ i] ] 就一定经过刷新,所以此时 f[ j - v[ i] ] 代表的是 “只考虑到前 i 个物品”,而不是我们需要的 “只考虑到前 i - 1 个物品”。那么如何在一次 j 循环中,保证用到的 f[ j - v[ i] ] 都没有刷新过呢?只能是从后往前循环了。 ♪(^∀^●)
所以,内层循环从二维压缩到一维就是:
for (int j = m; j >= 0; j--) { f[j] = f[j]; // 不选 if(j >= v[i]) // 可以选,比较选与不选的价值 f[j] = max(f[j], f[j - v[i]] + w[i]); }
进一步优化就是:
for (int j = m; j >= v[i]; j--) f[j] = max(f[j], f[j - v[i]] + w[i]);
f[] 初始化不同引起的含义不同:
① 初始化为:f[ 0 ] = 0;f[ j ] = 0;
此时 f[ j ] 代表如果当前考虑的是第 i 个物品,那么在总体积小于等于 j 的情况下,只考虑到前 i - 1 个物品的总价值最大是多少?
注意关键词:小于等于
② 初始化为:f[ 0 ] = 0;f[ j ] = -inf;
此时 f[ j ] 代表如果当前考虑的是第 i 个物品,那么在总体积恰好等于 j 的情况下,只考虑到前 i - 1 个物品的总价值最大是多少?
注意关键词:恰好等于
③ 原因:
证 :
如果是 ① 的情况:
1,我们可以从 f[ 0 ] = 0 ==》 f[ v[i] ] = max(f[ 0 ], w[i]) ==》 从而推导出所有 f[ j ],j <= m
2,同样的,设有 k < m,我们也可以从 f[ k ] = 0 ==》 f[ k + v[i] ] = max(f[ k ], w[i]) ==》 从而推导出所有 f[ j ],k <= j <= m
所以,第 2 种实际算出来的还要减去 k,才是实际物品需要占用的空间,但若存在这么两种组合,使得一种组合物品的空间少于另外一种组合物品的空间,但总价值一样且最大,则 f[ m ] 是可以由这两种状态推导出来的。
但如果是 ② 的情况,则会因为无穷小,使得 2 的情况不会发生,使得 一定是恰好等于。
④ 答案取值
如果是 ① 情况的话:f[ m ] 就是答案
如果是 ② 情况的话:需要比较所有 f[ j ],具体原因结合 ③ 以及下面代码中给出的测试案例分析。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 1010 #define inf 0x3f3f3f3f int n, m; // 物品数,背包体积 int f[N]; int v[N], w[N]; // 物品体积,物品价值 int max(int a, int b) { return a > b ? a : b; } int main(void) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d%d", &v[i], &w[i]); for (int i = 1; i <= m; i++) f[i] = -inf; for (int i = 1; i <= n; i++) { for (int j = m; j >= 1; j--) { if(j >= v[i]) f[j] = max(f[j], f[j - v[i]] + w[i]); printf("%d ", f[j]); }puts(""); } int res = 0; for (int i = 1; i <= m; i++) { printf("%d ", f[i]); res = max(res, f[i]); } /* 测试案例: 2 2 1 101 2 100 */ system("pause"); return 0; }
④ 小知识点:函数外面的全局变量是用堆保存的,而堆是全部初始化为 0 的
二,完全背包问题
三,多重背包问题
四,混合背包问题
五,二维费用的背包问题
六,分组背包问题
七,背包问题求方案数
八,求背包问题的方案
九,有依赖的背包问题
========== ========= ======== ====== ====== ===== ==== === == =