目录
关于背包问题,嗯一直学不明白,暑假咸的没事又拾起来学了一下,跟着这位大佬整理的思路(背包九讲——全篇详细理解与代码实现-CSDN博客),对背包的思想有了一定清晰的理解,大佬的文章有些长,所以跟着自己的思路再整理一下。
为了方便统一,先定义一下
c[i]:表示代价
w[i]:表示价值
dp[i][j]:表示前i个物品花费代价为j的可以获得的最大代价
p[i]:表示第i种物品最多有p[i]件
定义:
dp[i][j]:表示前i个物品恰放入一个容量为j的背包下可以获得的最大代价子问题第i1件物品状态:
①不选:dp[i][j]=dp[i-1][j] ②选:dp[i][j]=dp[i][j-c[i]]+w[i]状态转移方程:
dp[i][j]=max(dp[i-1][j],dp[i][j-c[i]]+w[i])优化空间复杂度:
O(V*N)
for(int i=1;i<=n;i++) for(int j=c[i];j<=V;j--) dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]);O(V)
for(int i=1;i<=n;i++) for(int j=V;j>=c[i];j++) dp[j]=max(dp[j],dp[j-c[i]]+w[i]);关于顺序和逆序:
逆序表示:dp[j]=max(dp[j],dp[j-c[i]]+w[i])由dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i])转移过来的 顺序表示:dp[j]=max(dp[j],dp[j-c[i]]+w[i])由dp[i][j]=max(dp[i][j],dp[i][j-c[i]]+w[i])转移过来的初始化问题:
①要求恰好装满:dp[i]=-∞,dp[0]=0; ②只要求价值最大:dp[i]=0;

- const int N = 1010;
- int c[N], w[N], dp[N];
- inline void solve()
- {
- int N, V;
- cin >> N >> V;
- for (int i = 1; i <= N; i++)
- cin >> c[i] >> w[i];
- for (int i = 1; i <= N; i++)
- for (int j = V; j >= c[i]; j--)
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
- cout << dp[V] << endl;
- }
- const int N = 5e4 + 10;
- int w[N], dp[N];
- inline void solve()
- {
- int C, H;
- cin >> C >> H;
- for (int i = 1; i <= H; i++)
- cin >> w[i];
- for (int i = 1; i <= H; i++)
- for (int j = C; j >= w[i]; j--)
- dp[j] = max(dp[j], dp[j - w[i]] + w[i]);
- cout << dp[C] << endl;
- }
- const int N = 1010;
- int c[N], w[N], dp[N];
- inline void solve()
- {
- int T, M;
- cin >> T >> M;
- for (int i = 1; i <= M; i++)
- cin >> c[i] >> w[i];
- for (int i = 1; i <= M; i++)
- for (int j = T; j >= c[i]; j--)
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
- cout << dp[T] << endl;
- }
定义:
dp[i][j]:表示前i种物品恰放入一个容量为j的背包下可以获得的最大代价子问题第i种物品状态:
①不选该种物品:dp[i][j]=dp[i-1][j]; ②选不同件该种物品:选0件、1件、2件……k件:dp[i][j]=dp[i-1][j-c[i]*k]+w[i]*k;状态转移方程:
dp[i][j]=max(dp[i-1][j-c[i]*k]+w[i]*k) 0<=c[i]*k<=j优化空间复杂度:
O(N*∑(V/c[i]))
for(int i=1;i<=n;i++) for(int j=c[i];j<=V;j++) for(int k=0;c[i]*k<=j;k++) dp[i][j]=max(dp[i][j],dp[i-1][j-c[i]*k]+w[i]*k); # 第一个参数,因为k=0时就相当于dp[i-1][j];O(V*N)转化为01背包问题
for(int i=1;i<=n;i++) for(int j=c[i];j<=j;j++) dp[j]=max(dp[j],dp[j-c[i]]+w[i]); //等价于dp[i][j]=max(dp[i-1][j],dp[i][j-c[i]]+w[i]);(不取该物品,取不同件);关于顺序和逆序:
逆序表示:dp[j]=max(dp[j],dp[j-c[i]]+w[i])由dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i])转移过来的 顺序表示:dp[j]=max(dp[j],dp[j-c[i]]+w[i])由dp[i][j]=max(dp[i-1][j],dp[i][j-c[i]]+w[i])转移过来的初始化问题:
①要求恰好装满:dp[i]=-∞,dp[0]=0; ②只要求价值最大:dp[i]=0;

- const int N = 1010;
- int c[N], w[N], dp[N];
- inline void solve()
- {
- int N, V;
- cin >> N >> V;
- for (int i = 1; i <= N; i++)
- cin >> c[i] >> w[i];
- for (int i = 1; i <= N; i++)
- for (int j = c[i]; j <= V; j++)
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
- cout << dp[V] << endl;
- }
- const int N = 1e6 + 10;
- int c[N], w[N], dp[N];
- inline void solve()
- {
- int s, n, d;
- cin >> s >> n >> d;
- for (int i = 1; i <= d; i++)
- cin >> c[i] >> w[i];
- while (n--)
- {
- for (int i = 1; i <= d; i++)
- for (int j = c[i]; j <= s; j++)
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
- s += dp[s];
- }
- cout << s << endl;
- }
- int main(

这个题目有个小坑

所以要做一下处理:除以1000防止爆空间
- const int N = 1e6 + 10;
- int c[N], w[N], dp[N];
- inline void solve()
- {
- int s, n, d;
- cin >> s >> n >> d;
- for (int i = 1; i <= d; i++)
- cin >> c[i] >> w[i];
- while (n--)
- {
- for (int i = 1; i <= d; i++)
- for (int j = c[i] / 1000; j <= s / 1000; j++)
- dp[j] = max(dp[j], dp[j - c[i] / 1000] + w[i]);
- s += dp[s / 1000];
- }
- cout << s << endl;
- }
定义:
dp[i][j]:表示前i种物品恰放入一个容量为j的背包下可以获得的最大代价子问题第i种物品状态:
①不选该种物品:dp[i][j]=dp[i-1][j]; ②选不同件该种物品:选1件、2件……p[i]件:dp[i][j]=dp[i-1][j-c[i]*k]+w[i]*k;状态转移方程:
dp[i][j]=max(dp[i-1][j-c[i]*k]+w[i]*k) 0<=k<=p[i]转化为01背包问题:
方法一:O(V*∑p[i])
for(int i=1;i<=n;i++) for(int j=V;j>=c[i];j--) for(int k=1;c[i]*k<=j&&k<=p[i];k++) dp[j]=max(dp[j],dp[j-c[i]*k]+w[i]*k); # 第一个参数,因为k=0时就相当于dp[i-1][j];方法二:二进制优化O(N*log(p)*V)
for (int i = 1; i <= N; i++) { int a, b, s; cin >> a >> b >> s; int k = 1; while (k <= s) //0……2^k-1部分的系数1,2,4,8…… { cnt++; c[cnt] = k * a; w[cnt] = k * b; s -= k; k *= 2; } if (s > 0) //2^k……s部分的系数 s-2^k { cnt++; c[cnt] = s * a; w[cnt] = s * b; } } N = cnt; //更新总数量 for (int i = 1; i <= N; i++) //01背包问题 for (int j = V; j >= c[i]; j--) dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
for (int i = 1; i <= n; i++) { cin >> c[i] >> w[i] >> p[i]; int s = min(p[i], W / w[i]); for (int k = 1; s > 0; k <<= 1) { k = min(k, s); s -= k; for (int j = W; j >= k * w[i]; j--) { dp[j] = max(dp[j], dp[j - k * w[i]] + k * c[i]); } } }初始化问题:
①要求恰好装满:dp[i]=-∞,dp[0]=0; ②只要求价值最大:dp[i]=0;
方法一:

- const int N = 110;
- int c[N], w[N], p[N], dp[N];
- inline void solve()
- {
- int N, V;
- cin >> N >> V;
- int cnt = 0;
- for (int i = 1; i <= N; i++)
- cin >> c[i] >> w[i] >> p[i];
- for (int i = 1; i <= N; i++)
- for (int j = V; j >= c[i]; j--)
- for (int k = 1; c[i] * k <= j && k <= p[i]; k++)
- dp[j] = max(dp[j], dp[j - c[i] * k] + w[i] * k);
- cout << dp[V] << endl;
- }
方法二:

- const int N = 20010; //注意初始化,否则会越界
- int c[N], w[N], dp[N];
- inline void solve()
- {
- int N, V;
- cin >> N >> V;
- int cnt = 0;
- for (int i = 1; i <= N; i++)
- {
- int a, b, s;
- cin >> a >> b >> s;
- int k = 1;
- while (k <= s) //0……2^k-1部分的系数1,2,4,8……
- {
- cnt++;
- c[cnt] = k * a;
- w[cnt] = k * b;
- s -= k;
- k *= 2;
- }
- if (s > 0) //2^k……s部分的系数 s-2^k
- {
- cnt++;
- c[cnt] = s * a;
- w[cnt] = s * b;
- }
- }
- N = cnt; //更新总数量
- for (int i = 1; i <= N; i++) //01背包问题
- for (int j = V; j >= c[i]; j--)
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
- cout << dp[V] << endl;
- }
- const int N = 1e6 + 10; // 注意初始化,否则会越界
- int c[N], w[N], dp[N];
- inline void solve()
- {
- int n, W;
- cin >> n >> W;
- int cnt = 0;
- for (int i = 1; i <= n; i++)
- {
- int a, b, s;
- cin >> a >> b >> s;
- int k = 1;
- while (k <= s)
- {
- cnt++;
- w[cnt] = k * a;
- c[cnt] = k * b;
- s -= k;
- k *= 2;
- }
- if (s > 0)
- {
- cnt++;
- w[cnt] = s * a;
- c[cnt] = s * b;
- }
- }
- n = cnt;
- for (int i = 1; i <= n; i++)
- for (int j = W; j >= c[i]; j--)
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
- cout << dp[W] << endl;
- }
简化
- const int N = 1e6 + 10; // 注意初始化,否则会越界
- int c[N], w[N], p[N], dp[N];
- inline void solve()
- {
- int n, W;
- cin >> n >> W;
- for (int i = 1; i <= n; i++)
- {
- cin >> c[i] >> w[i] >> p[i];
- int s = min(p[i], W / w[i]);
- for (int k = 1; s > 0; k <<= 1)
- {
- k = min(k, s);
- s -= k;
- for (int j = W; j >= k * w[i]; j--)
- {
- dp[j] = max(dp[j], dp[j - k * w[i]] + k * c[i]);
- }
- }
- }
- cout << dp[W] << endl;
- }
01背包、完全背包、多重背包的混合状态转移:
for (int i = 1; i <= N; i++) { cin >> c[i] >> w[i] >> p[i]; // 01背包 if (p[i] == -1) for (int j = V; j >= c[i]; j--) dp[j] = max(dp[j], dp[j - c[i]] + w[i]); // 完全背包 else if (p[i] == 0) for (int j = c[i]; j <= V; j++) dp[j] = max(dp[j], dp[j - c[i]] + w[i]); // 多重背包二进制优化 else { int s = min(p[i], V / c[i]); for (int k = 1; s > 0; k <<= 1) { k = max(k, s); s -= k; for (int j = V; j >= k * c[i]; j--) dp[j] = max(dp[j], dp[j - k * c[i]] + k * w[i]); } } }

- const int N = 1e6 + 10; // 注意初始化,否则会越界
- int c[N], w[N], p[N], dp[N];
- inline void solve()
- {
- int N, V;
- cin >> N >> V;
- for (int i = 1; i <= N; i++)
- {
- cin >> c[i] >> w[i] >> p[i];
- // 01背包
- if (p[i] == -1)
- for (int j = V; j >= c[i]; j--)
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
- // 完全背包
- else if (p[i] == 0)
- for (int j = c[i]; j <= V; j++)
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);//或将完全背包转化为多重01背包s=V/c[i]
- // 多重背包二进制优化
- else
- {
- int s = min(p[i], V / c[i]);
- for (int k = 1; s > 0; k <<= 1)
- {
- k = min(k, s);
- s -= k;
- for (int j = V; j >= k * c[i]; j--)
- dp[j] = max(dp[j], dp[j - k * c[i]] + k * w[i]);
- }
- }
- }
- cout << dp[V] << endl;
- }
- const int N = 1e6 + 10; // 注意初始化,否则会越界
- int c[N], w[N], p[N], dp[N];
- inline void solve()
- {
- int m1, m2, s1, s2, N;
- scanf("%d:%d %d:%d %d", &m1, &s1, &m2, &s2, &N);
- int V = m2 * 60 + s2 - m1 * 60 - s1;
- for (int i = 1; i <= N; i++)
- {
- cin >> c[i] >> w[i] >> p[i];
- int s;
- if (p[i] == 0) // 完全转化为多重
- s = V / c[i];
- else
- s = min(p[i], V / c[i]);
- for (int k = 1; s > 0; k <<= 1)
- {
- k = min(k, s);
- s -= k;
- for (int j = V; j >= k * c[i]; j--)
- dp[j] = max(dp[j], dp[j - k * c[i]] + k * w[i]);
- }
- }
- cout << dp[V] << endl;
- }
定义:每件物品需要同时花费两种不同的代价
dp[i][j][k]:表示前i种物品付出两种代价分别最大为j和k时可获得的最大价值状态转移方程:
dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-c[i]][k-m[i]]+w[i])01背包代码(完全背包、多重背包可以类比)
for(int i=1;i<=n;i++) for(int j=V;j>=c[i];j--) for(int k=M;k>=m[i];k--) dp[j][k]=max(dp[j][k],dp[j-c[i]][k-m[i]]+w[i]);

- const int N = 1010; // 注意初始化,否则会越界
- int c[N], w[N], m[N], dp[N][N];
- inline void solve()
- {
- int N, V, M;
- cin >> N >> V >> M;
- for (int i = 1; i <= N; i++)
- {
- cin >> c[i] >> m[i] >> w[i];
- for (int j = V; j >= c[i]; j--)
- for (int k = M; k >= m[i]; k--)
- dp[j][k] = max(dp[j][k], dp[j - c[i]][k - m[i]] + w[i]);
- }
- cout << dp[V][M] << endl;
- }
- const int N = 1010; // 注意初始化,否则会越界
- int c[N], w[N], m[N], dp[N][N];
- inline void solve()
- {
- int V, M, N;
- cin >> V >> M >> N;
- for (int i = 1; i <= N; i++)
- {
- cin >> c[i] >> m[i] >> w[i];
- for (int j = V; j >= c[i]; j--)
- for (int k = M; k >= m[i]; k--)
- dp[j][k] = max(dp[j][k], dp[j - c[i]][k - m[i]] + w[i]);
- }
- cout << dp[V][M] << endl;
- }
定义:
dp[k][j]:表示前k组物品花费代价j能取得的最大价值子问题第k组物品状态:
①不选该组物品:dp[k][j]=dp[k-1][j]; ②选该组物品:dp[k][j]=dp[k-1][j-c[i]+w[i]] 物品i属于k组状态转移方程:
dp[k][j]=max(dp[k-1][j],dp[k-1][j-c[i]]+w[i])模板:
for (int k = 1; k <= N; k++) { int s; cin >> s; // 第k组的物品数量 for (int i = 1; i <= s; i++) cin >> c[i] >> w[i]; // 组中每个物品i的属性 for (int j = V; j >= 0; j--) for (int i = 1; i <= s; i++) // 保证每组物品只能选一个,可以覆盖之前组内物品最优解的来取最大值 if (j >= c[i]) dp[j] = max(dp[j], dp[j - c[i]] + w[i]); }

- const int N = 110; // 注意初始化,否则会越界
- int c[N], w[N], m[N], dp[N];
- inline void solve()
- {
- int N, V;
- cin >> N >> V;
- for (int k = 1; k <= N; k++)
- {
- int s;
- cin >> s; // 第k组的物品数量
- for (int i = 1; i <= s; i++)
- cin >> c[i] >> w[i]; // 组中每个物品i的属性
- for (int j = V; j >= 0; j--)
- for (int i = 1; i <= s; i++) // 保证每组物品只能选一个,可以覆盖之前组内物品最优解的来取最大值
- if (j >= c[i])
- dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
- }
- cout << dp[V] << endl;
- }
- const int N = 110; // 注意初始化,否则会越界
- const int M = 1010; // 注意初始化,否则会越界
- int c[M], w[M], dp[M];
- int g[N][N], b[M]; // g[k][i]表示小组k种第i个物品的编号,b[k]表示小组k的物品+1;
- inline void solve()
- {
- int N, V;
- cin >> V >> N;
- int t = 0, k = 0;
- for (int i = 1; i <= N; i++)
- {
- cin >> c[i] >> w[i] >> k;
- t = max(t, k); // 求小组的组数
- b[k]++; // 小组k的物品+1;
- g[k][b[k]] = i; // 小组k中第b[k]个物品的编号为i;
- }
- for (int k = 1; k <= t; k++)
- for (int j = V; j >= 0; j--)
- for (int i = 1; i <= b[k]; i++)
- if (j >= c[g[k][i]])
- dp[j] = max(dp[j], dp[j - c[g[k][i]]] + w[g[k][i]]);
-
- cout << dp[V] << endl;
- }