这里顺便引入基础版各背包模型:背包问题详解
进阶版: 背包计数问题
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品只能选一个,求总体积不超过 m 的最大价值
输入 :n 个物品, 总体积不超过 m, 接下来是 n 个物品的体积和价值
4 5
1 2
2 4
3 4
4 5
输出:最大价值
8
二维代码
#includeusing namespace std; const int N = 110; int n, m; int f[N][N]; int main() { cin >> n >> m; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = 0; j <= m; j ++ ) { f[i][j] = f[i - 1][j]; //不选 if (j >= v) f[i][j] = max(f[i][j], f[i - 1][j - v] + w); } } cout << f[n][m]; return 0; }
状态压缩
#include2、从前 i 个物品中选,且总体积恰好是 j 01背包问题using namespace std; const int N = 110; int n, m; int f[N]; int main() { cin >> n >> m; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = m; j >= v; j -- ) { f[j] = max(f[j], f[j - v] + w); } } cout << f[m]; return 0; }
(1)求价值最大值:初始化 f[0][0] = 0, 其余是 -INF
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品只能选一个,求总体积恰好是 j 的最大价值
输入
4 5
1 2
2 4
3 4
4 5
输出
8
二维代码
#includeusing namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N][N]; int main() { cin >> n >> m; memset(f, -0x3f, sizeof f); f[0][0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = 0; j <= m; j ++ ) { f[i][j] = f[i - 1][j]; //先不选从上一状态转移过来,即最开始一个物品的话,就是从第 0 行转移,不能敲好等于自身体积,则会被置为 -INF if (j >= v) f[i][j] = max(f[i][j], f[i - 1][j - v] + w); //再判断当前是否可以加入当前这个物品 } } cout << f[n][m]; return 0; }
状态压缩
#includeusing namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N]; int main() { cin >> n >> m; memset(f, -0x3f, sizeof f); f[0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = m; j >= v; j -- ) { f[j] = max(f[j], f[j - v] + w); } } cout << f[m]; return 0; }
(2)求价值最小值:初始化 f[0][0] = 0, 其余是 INF
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品只能选一个,求总体积恰好是j的最小价值
输入
4 5
1 2
2 4
3 4
4 5
输出
7
二维代码
#includeusing namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N][N]; int main() { cin >> n >> m; memset(f, 0x3f, sizeof f); f[0][0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = 0; j <= m; j ++ ) { f[i][j] = f[i - 1][j]; if (j >= v) f[i][j] = min(f[i][j], f[i - 1][j - v] + w); } } cout << f[n][m]; return 0; }
状态压缩
#include完全背包问题 (1)求价值最大值:初始化 f[0][0] = 0, 其余是 -INFusing namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N]; int main() { cin >> n >> m; memset(f, 0x3f, sizeof f); f[0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = m; j >= v; j -- ) { f[j] = min(f[j], f[j - v] + w); } } cout << f[m]; return 0; }
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品可以选无数多个,求总体积恰好是 j 的最大价值
输入
4 5
1 2
2 4
3 4
4 5
输出
10
二维代码
#include☆完全背包问题的经典状态压缩过程#include using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N][N]; int main() { cin >> n >> m; memset(f, -0x3f, sizeof f); f[0][0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = 0; j <= m; j ++ ) { f[i][j] = f[i - 1][j]; if (j >= v) f[i][j] = max(f[i][j], f[i][j - v] + w); } } cout << f[n][m] << endl; return 0; }
f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-3*v]+2*w , .....)
由上两式,可得出如下递推关系:
f[i][j]=max(f[i,j-v] + w , f[i-1][j]) //即选与不选两种状态
状态压缩
#include(2)求价值最小值:初始化 f[0][0] = 0, 其余是 INF#include using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N]; int main() { cin >> n >> m; memset(f, -0x3f, sizeof f); f[0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = v; j <= m; j ++ ) { f[j] = max(f[j], f[j - v] + w); } } cout << f[m] << endl; return 0; }
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品可以选无数多个,求总体积恰好是 j 的最小价值
输入
4 5
1 2
2 4
3 4
4 5
输出
7
二维代码
#includeusing namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N][N]; int main() { cin >> n >> m; memset(f, 0x3f, sizeof f); f[0][0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = 0; j <= m; j ++ ) { f[i][j] = f[i - 1][j]; if (j >= v) f[i][j] = min(f[i][j], f[i][j - v] + w); } } cout << f[n][m]; return 0; }
状态压缩
#include3、从前 i 个物品中选,且总体积至少是 j ,初始化是 f[0][0] = 0, 其余是 INF(只会求价值的最小值)#include using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N]; int main() { cin >> n >> m; memset(f, 0x3f, sizeof f); f[0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = v; j <= m; j ++ ) { f[j] = min(f[j], f[j - v] + w); } } cout << f[m] << endl; return 0; }
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品可以选无数多个,求总体积至少是j的最小价值
输入
4 5
1 2
2 4
3 4
4 5
输出
7
题解
1.初始化时:和恰好等于体积 j 的初始化方式相同,因为至少包含了敲好等于,而计算超出部分时,则需要通过 f[i][0] 转移过来 2.max(0, j - v) ,即当体积此时不能装下时, 表示超出了,也满足,所以直接可以从f[i][0] 表示过来,再 + w, 表示可以装,只是体积超出了。 第一种背包( 1 2 ): 0 2 4 6 8 10 第二个背包( 2 4 ): 0 2 4 6 8 10 第三个背包( 3 4 ): 0 2 4 4 6 8 第四个背包( 4 5 ): 0 2 4 4 5 7
二维代码
#include#include using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N][N]; int main() { cin >> n >> m; memset(f, INF, sizeof f); f[0][0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = 0; j <= m; j ++ ) { f[i][j] = min(f[i - 1][j], f[i][max(0, j - v)] + w);//即使物品体积比j大,j - v < 0,也能选,等价于f[i - 1][0] } } cout << f[n][m] << endl; return 0; }
状态压缩
#include#include using namespace std; const int N = 110, INF = 0x3f3f3f3f; int n, m; int f[N]; int main() { cin >> n >> m; memset(f, 0x3f, sizeof f); f[0] = 0; for (int i = 1; i <= n; i ++ ) { int v, w; cin >> v >> w; for (int j = m; j >= 0; j -- ) { f[j] = min(f[j], f[max(0, j - v)] + w);//即使物品体积比j大,j - v < 0,也能选,等价于f[0] } } cout << f[m] << endl; return 0; }
如有错误之处,欢迎指正~~~



