0/1 背包问题是一种经典的计算机科学和数学问题,也是动态规划的典型应用。问题描述如下:
问题描述
给定一个容量为 ( W ) 的背包和 ( n ) 个物品,每个物品有一个重量 ( w_i ) 和价值 ( v_i )。每个物品只能被选择一次(即不能部分选取)。目标是选择一些物品装入背包,使得在不超过背包容量的前提下,这些物品的总价值最大。
形式化定义
-
输入:
- ( W ):背包的最大容量。
- ( n ):物品的数量。
- ( w[i] ):第 ( i ) 个物品的重量。
- ( v[i] ):第 ( i ) 个物品的价值。
-
输出:
- 能装入背包的物品的最大总价值。
示例
假设有如下物品和背包:
- 背包容量 ( W = 10 )
- 物品列表:
- 物品 1:重量 2,价值 3
- 物品 2:重量 3,价值 4
- 物品 3:重量 4,价值 5
- 物品 4:重量 5,价值 8
通过选择物品 1、物品 2 和物品 4,可以达到最大价值 3 + 4 + 8 = 15,总重量为 10。
动态规划解法
为了使用动态规划解决 0/1 背包问题,我们定义一个二维数组 dp
,其中 dp[i][j]
表示前 ( i ) 个物品在容量不超过 ( j ) 的情况下能够取得的最大价值。
状态转移方程
[ dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]) ]
其中:
dp[i-1][j]
表示不选择第 ( i ) 个物品的情况。dp[i-1][j-w[i]] + v[i]
表示选择第 ( i ) 个物品的情况。
初始条件
[ dp[0][j] = 0 ]
表示没有物品可选时,所有容量的最大价值为 0。
C++ 实现
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 0/1 背包问题的解决方案
int knapsack(int W, const vector<int>& weights, const vector<int>& values, int n) {
// 定义 DP 数组
vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));
// 填充 DP 数组
for (int i = 1; i <= n; ++i) {
for (int w = 1; w <= W; ++w) {
if (weights[i - 1] <= w) {
dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]);
} else {
dp[i][w] = dp[i - 1][w];
}
}
}
// 返回背包最大价值
return dp[n][W];
}
int main() {
int W = 10; // 背包容量
vector<int> weights = {2, 3, 4, 5}; // 物品重量
vector<int> values = {3, 4, 5, 8}; // 物品价值
int n = weights.size(); // 物品数量
cout << "Maximum value in Knapsack: " << knapsack(W, weights, values, n) << endl;
return 0;
}
代码解释
-
定义 DP 数组:
vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));
:创建一个大小为(n + 1) x (W + 1)
的二维数组,初始值全为 0。
-
填充 DP 数组:
- 遍历每个物品
i
和每个容量w
。 - 如果当前物品的重量
weights[i - 1]
小于等于当前容量w
,则选择max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1])
。 - 否则,保持
dp[i][w] = dp[i - 1][w]
。
- 遍历每个物品
-
返回结果:
dp[n][W]
表示在背包容量为W
的情况下,能取得的最大价值。
总结
0/1 背包问题通过动态规划方法,可以在多项式时间内解决,这使得它在处理大规模数据时也具有较高的效率和准确性。该问题及其解决方案在许多优化和资源分配问题中都有广泛的应用。