第13章:背包问题 (Knapsack Problem)
背包问题是动态规划中的一类经典问题,也是各类机试和竞赛中的常客。它描述了在给定容量的背包和一组具有价值和重量(或体积、成本)的物品时,如何选择物品放入背包以使得总价值最大化。背包问题的变种极多,但其核心思想和解法范式是相通的。掌握了背包问题,不仅能解决一系列相关题目,更能加深对动态规划“状态”与“选择”精髓的理解。
本章将重点介绍三种最核心的背包模型:0-1背包、完全背包和多重背包,并详细讲解如何通过状态压缩(滚动数组)技巧来优化空间复杂度。
13.1 0-1背包
问题描述
这是最简单的背包模型。我们有 NNN 件物品和一个容量为 VVV 的背包。第 iii 件物品的成本(或重量)是 wiw_iwi,价值是 viv_ivi。每件物品只有一件,你可以选择将它放入背包,也可以选择不放。求解将哪些物品装入背包,可使这些物品的成本总和不超过背包容量,且价值总和最大。
核心思想与DP分析
0-1背包的“0-1”正体现在每件物品只有两种选择:不放(0)或放(1)。这是典型的动态规划应用场景。
-
状态表示 (
dp数组的含义):
我们定义一个二维数组dp[i][j]。它的含义是:从前 iii 件物品中任意选择,放入一个容量为 jjj 的背包中所能获得的最大价值。我们的最终目标就是求dp[N][V]。 -
状态转移方程:
当我们面对第 iii 件物品时,需要决定是否将其放入容量为 jjj 的背包中。此时,我们有两种决策:- 不放第 iii 件物品:如果我们不放第 iii 件物品,那么问题就转化为:用前 i−1i-1i−1 件物品来填满容量为 jjj 的背包。此时的最大价值就是
dp[i-1][j]。 - 放第 iii 件物品:要放下第 iii 件物品,前提是背包的当前容量 jjj 必须大于等于物品 iii 的成本 wiw_iwi。如果满足这个条件,我们获得的价值就是物品 iii 本身的价值 viv_ivi,加上用前 i−1i-1i−1 件物品来填满剩余容量 j−wij - w_ij−wi 的背包所能获得的最大价值,即
v_i + dp[i-1][j - w_i]。
既然要求最大价值,我们自然是在这两种决策中选择更优的一个。因此,状态转移方程如下:
dp[i][j]=max(dp[i−1][j],dp[i−1][j−wi]+vi)(j≥wi)dp[i][j] = \max(dp[i-1][j], \quad dp[i-1][j - w_i] + v_i) \quad (j \ge w_i)dp[i][j]=max(dp[i−1][j],dp[i−1][j−wi]+vi)(j≥wi)
- 不放第 iii 件物品:如果我们不放第 iii 件物品,那么问题就转化为:用前 i−1i-1i−1 件物品来填满容量为 jjj 的背包。此时的最大价值就是
-
初始化:
当没有物品可选 (i=0i=0i=0) 或者背包容量为0 (j=0j=0j=0) 时,最大价值显然为0。因此,可以将整个dp数组初始化为0。
C++实现 (二维数组)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 101;
const int MAXV = 1001;
int w[MAXN]; // 物品的成本
int v[MAXN]; // 物品的价值
int dp[MAXN][MAXV]; // dp[i][j]表示前i个物品放入容量为j的背包的最大价值
int main() {
int N, V;
cin >> N >> V; // 物品数量和背包容量
for (int i = 1; i <= N; ++i) {
cin >> w[i] >> v[i];
}
// 初始化dp数组,在C++中全局数组默认初始化为0,这里可以省略
// for (int i = 0; i <= N; ++i) {
// for (int j = 0; j <= V; ++j) {
// dp[i][j] = 0;
// }
// }
// 开始动态规划
for (int i = 1; i <= N; ++i) {
for (int j = 0; j <= V; ++j) {
// 首先,不放第i件物品
dp[i][j

最低0.47元/天 解锁文章
638

被折叠的 条评论
为什么被折叠?



