目标函数: maximize∑i=1nvixi
约束条件: ∑i=1nwixi≤C xi∈{0,1}for all i
其中:
-
vi 是物品 i 的价值
-
wi 是物品 i 的重量
-
xi 是一个0/1变量,表示物品 i 是否被选中(1表示选中,0表示未选中)
3. 动态规划的基本思想
01背包问题可以通过动态规划(Dynamic Programming, DP)来高效求解。动态规划的核心思想是将问题分解为子问题,并通过存储子问题的解来避免重复计算。
对于01背包问题,我们可以定义一个二维数组 dp[i][j],表示前 i 个物品在容量为 j 的背包中所能获得的最大价值。
状态转移方程: dp[i][j]=max(dp[i−1][j],dp[i−1][j−wi]+vi)
解释:
-
如果不选择第 i 个物品,则最大价值为 dp[i−1][j]
-
如果选择第 i 个物品,则最大价值为 dp[i−1][j−wi]+vi
初始条件:
-
dp[0][j]=0 (没有物品时,价值为0)
-
dp[i][0]=0 (背包容量为0时,价值为0)
4. 动态规划表的构建
动态规划表的构建过程如下:
-
初始化一个 (n+1)×(C+1) 的二维数组 dp,所有值初始化为0。
-
遍历每个物品 i(从1到n)。
-
对于每个物品 i,遍历背包容量 j(从0到C)。
-
对于每个 j,根据状态转移方程更新 dp[i][j]。
5. 代码实现
以下是基于动态规划的01背包问题的C++代码实现:
#include <iostream>
using namespace std;
int main() {
int n, c;
cin >> n >> c;
// 动态规划表
int dp[n + 1][c + 1];
// 物品重量和价值
int w[n], v[n];
// 选择的物品
int x[n];
// 初始化动态规划表
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= c; j++) {
dp[i][j] = 0;
}
}
// 输入物品重量
for (int i = 0; i < n; i++) {
cin >> w[i];
}
// 输入物品价值
for (int i = 0; i < n; i++) {
cin >> v[i];
}
// 填充动态规划表
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= c; j++) {
if (j < w[i - 1]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
}
}
}
// 输出最大价值
cout << dp[n][c] << endl;
// 回溯选择的物品
int j = c;
int h = 0;
for (int i = n; i > 0; i--) {
if (dp[i][j] != dp[i - 1][j]) {
x[h++] = i;
j -= w[i - 1];
}
}
// 输出选择的物品
for (int i = h - 1; i >= 0; i--) {
cout << "x" << x[i] << " ";
}
cout << endl;
return 0;
}
6. 代码解析
-
输入部分:
-
首先输入物品数量 n 和背包容量 C。
-
然后输入每个物品的重量 w 和价值 v。
-
-
动态规划表的初始化:
-
创建一个 (n+1)×(C+1) 的二维数组 dp,所有值初始化为0。
-
-
填充动态规划表:
-
对每个物品 i,遍历背包容量 j。
-
如果当前容量 j 小于物品 i 的重量,则不选择该物品。
-
否则,选择该物品或不选择该物品,取两者中的最大值。
-
-
回溯选择的物品:
-
从动态规划表的最后一个状态开始,回溯哪些物品被选中。
-
如果 dp[i][j] 不等于 dp[i−1][j],说明物品 i 被选中,记录下来并更新剩余容量。
-
7. 样例输入与输出
样例输入:
5
10
2 2 6 5 4
6 3 5 4 6
样例输出:
15
x1 x2 x5
解释:
-
最大价值为15。
-
选择的物品是第1个、第2个和第5个,它们的总重量为 2+2+4=8,总价值为 6+3+6=15。
8. 时间与空间复杂度
-
时间复杂度:O(n×C)
-
需要填充一个 (n+1)×(C+1) 的二维数组。
-
-
空间复杂度:O(n×C)
-
需要存储一个 (n+1)×(C+1) 的二维数组。
-
9. 优化与扩展
-
空间优化:
-
可以将二维数组优化为一维数组,通过从后向前更新的方式减少空间复杂度。
-
-
多重背包问题:
-
如果每个物品可以选多次(最多 k 次),可以扩展为多重背包问题。
-
-
完全背包问题:
-
如果每个物品可以无限次选择,可以扩展为完全背包问题。
-
10. 总结
01背包问题是一个经典的动态规划问题,通过将问题分解为子问题并存储中间结果,可以高效地求解。动态规划的核心在于状态转移方程的设计,以及如何通过回溯找到具体的解。通过本文的讲解和代码实现,读者可以深入理解01背包问题的解决方法,并将其应用到实际问题中。