for (int i = 1; i <= n; i++)//第i件物品 for (int j = c[i]; j <= V; j++)//背包体积 for (int k = 0; k * c[i] <= j; k++)//决策数 dp[i][j] = max(dp[i][j], dp[i - 1][j - k * c[i]] + k * w[i]); /* for 物品 for 体积 for 决策 */
注意取 max 时与01背包不同,max 的第一个参数不再是dp[i-1][j],因为dp[i][j]要在取多件的情况中取最值; 当 k = 0时,dp[i-1][j - k*c[i]] + k*w[i]就相当于dp[i-1][j],也就是这件物品一件也不取的情况
时间复杂度为Θ(N ∗ Σ(V/c[i]))
多重背包
题目大意
有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 p [ i ] 件可用,每件费用是c [ i ] ,价值是w [ i ] 。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
思路
对于第 i 种物品有 p[i]+1种策略:取0...p[i]件,令dp[i][j]表示前 i 种物品恰放入一个容量为 j 的背包的最大价值,则有状态转移方程
#include<bits/stdc++.h> usingnamespace std; #define ll long long int constint maxn = 1e5 + 10; int dp[maxn], c[maxn], w[maxn], p[maxn]; int n, m; voidsolve() { cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> c[i] >> w[i] >> p[i];
for (int i = 1; i <= n; ++i) { for (int j = m; j >= 0; --j) { for (int k = 1; k * c[i] <= j && k <= p[i]; ++k) { dp[j] = max(dp[j], dp[j - c[i] * k] + w[i] * k); } } }
int res = 0; for (int i = 1; i <= m; ++i) res = max(res, dp[i]); cout << res << endl; }
#include<bits/stdc++.h> usingnamespace std; #define ll long long int constint maxn = 1e5 + 10; int n, m; int c[maxn], w[maxn], p[maxn], dp[maxn];
structnode { int c, w; } good[maxn];
voidsolve() { cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> c[i] >> w[i] >> p[i];
for (int i = 1; i <= n; ++i) { int cnt = 1; // 分成2^k次方的时候 // 新物品的体积和价值 = 选的个数 *单个的体积和价格 for (int z = 1; z <= p[i]; z *= 2, ++cnt) { good[cnt].c = z * c[i]; good[cnt].w = z * w[i]; p[i] -= z; }
//变成了01背包 for (int j = 1; j < cnt;++j)//遍历选法 { for (int k = m; k >= good[j].c;k--)//遍历体积,由于是滚动一维,所以要逆序遍历 { dp[k]=max(dp[k],dp[k-good[j].c]+good[j].w); } } }
#include<stdio.h> #include<algorithm> #include<vector> usingnamespace std; constint N = 1005; int dp[N][N], q[N]; structnode { int wei, val; node(int a, int b) { wei = a, val = b; } }; vector<node> ve[N];
intmain() { int n, m; scanf("%d%d", &m, &n); int we = 0; for (int i = 1; i <= n; i++) { int a, b, c; scanf("%d%d%d", &a, &b, &c); ve[c].push_back(node(a, b)); we = max(we, c); } for (int i = 0; i <= we; i++) for (int j = 0; j <= m; j++) dp[i][j] = -1e9;
dp[0][0] = 0; // 遍历物品 for (int i = 1; i <= we; i++) { // 遍历体积 for (int j = 0; j <= m; j++) { // 遍历决策 dp[i][j] = dp[i - 1][j]; for (int k = 0; k < ve[i].size(); k++) { if (ve[i][k].wei <= j) dp[i][j] = max(dp[i][j], dp[i - 1][j - ve[i][k].wei] + ve[i][k].val); } } }
int res = 0; for (int i = 0; i <= m; i++) res = max(res, dp[we][i]); printf("%d\n", res); return0; }
有依赖的背包问题
这种背包问题的物品间存在某种“依赖”的关系。也就是说,i 依赖于 j ,表示若选物品 i ,则必须选物品 j 。
#include<stdio.h> #include<algorithm> #include<vector> usingnamespace std; constint N = 4e3 + 5, M = 65; vector<int> ve[M]; int wei[M], imp[M];//价格和重要度 int n, m; int dp[M][N]; intmain() { scanf("%d%d", &m, &n); m /= 10; for (int i = 1; i <= n; i++) { int c; scanf("%d%d%d", &wei[i], &imp[i], &c); wei[i] /= 10; ve[c].push_back(i); }
// 先处理给出每个主件分配不同空间时对应获得的最大价值 // 遍历各主件 for (int u = 1; u <= n; u++) { // 01背包问题----(这里滚动了变成了一个维数组) // dp[u][j] 表示第 u 个背包,分配 j 体积 可以获得的最大价值 // 遍历附件(物品数量) for (int j = 0; j < ve[u].size(); j++) { int to = ve[u][j]; for (int k = m; k >= wei[to]; k--) { dp[u][k] = max(dp[u][k], dp[u][k - wei[to]] + imp[to] * wei[to] * 10); } } }
// 整合 // 遍历主件 for (int i = 0; i < ve[0].size(); i++) { int to = ve[0][i]; for (int j = m; j >= wei[to]; j--)//逆序! { // 遍历给当前主件的附件分配的空间 for (int k = j - wei[to]; k >= 0; k--) { dp[0][j] = max(dp[0][j], dp[to][k] + imp[to] * wei[to] * 10 + dp[0][j - k - wei[to]]); } } } printf("%d\n", dp[0][m]);
#include<bits/stdc++.h> usingnamespace std; #define ll long long int constint maxn = 1e3 + 10; int dp[maxn][maxn], n, m, w[maxn], c[maxn]; voidsolve(){ cin >> n >> m; for (int i = 1; i <= n;++i) cin >> c[i] >> w[i];
for (int i = n; i >= 1;--i)//逆序,使得编号小的可以从编号大的推算而出 { for (int j = 0; j <= m;++j) { dp[i][j] = dp[i + 1][j];
#include<stdio.h> #include<algorithm> usingnamespace std; int dp[1005][35], wei[105], val[105]; intmain() { int n, v, k; scanf("%d%d%d", &n, &v, &k); for (int i = 1; i <= n; i++) scanf("%d", &val[i]); for (int i = 1; i <= n; i++) scanf("%d", &wei[i]);
for (int i = 1; i <= v; i++) for (int j = 1; j <= k; j++) dp[i][j] = 0;
for (int i = 1; i <= n; i++) { for (int j = v; j >= wei[i]; j--) { //a数组记录的是不选第 i 个物品的 k 个最大价值 //b数组记录的是选第 i 个物品的 k 个最大价值 int a[35] = {0}, b[35] = {0}; for (int l = 1; l <= k; l++) { a[l] = dp[j][l]; b[l] = dp[j - wei[i]][l] + val[i]; } a[k + 1] = b[k + 1] = -1;
int ai = 1, bi = 1; for (int l = 1; l <= k && (a[ai] != -1 || b[bi] != -1); l++) { if (a[ai] > b[bi]) dp[j][l] = a[ai++]; else dp[j][l] = b[bi++]; //相同的数算同一个 if (dp[j][l] == dp[j][l - 1]) l--; } } } printf("%d\n", dp[v][k]); return0; }