问题定义:
0-1背包问题是指在给定一组物品和一个背包的容量下,如何选择物品放入背包,使得物品的总价值最大,同时保证总重量不超过背包的容量。
动态规划基本思想:
动态规划是一种将复杂问题分解成更小且相互重叠的子问题的优化算法。在解决0-1背包问题时,可以利用动态规划的思想将问题分解为多个子问题,并通过维护一个二维数组来记录每个子问题的最优解。
解题步骤:
(1)确定状态:
将背包的容量以及物品的数量作为状态变量进行建模,可以定义一个二维数组dp[i][j]来表示在前i个物品中,背包容量为j时的最大总价值。
(2)初始化边界:
当背包容量为0或者没有物品可选择时,最大总价值为0,因此可以将dp[i][0]和dp[0][j]的初始值设为0。
(3)状态转移方程:
对于每一个物品i,可以选择将其放入背包或者不放入背包。若选择放入背包,则背包的总价值应为前i-1个物品放入容量为j-w[i]的背包时的最大总价值加上物品i的价值v[i];若选择不放入背包,则背包的总价值应为前i-1个物品放入容量为j的背包时的最大总价值。因此,可以根据这两种情况来更新dp[i][j]的值:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])
其中,w[i]和v[i]分别表示第i个物品的重量和价值。
(4)求解最优解:
通过计算dp数组中的元素,可以得到在给定背包容量下的最大总价值。同时,可以通过回溯的方式确定具体选取了哪些物品。
例子说明: 假设有5个物品,它们的重量和价值分别为:
物品1:重量2,价值3
物品2:重量3,价值4
物品3:重量4,价值5
物品4:重量5,价值8
物品5:重量9,价值10
背包的容量为10,我们要确定如何选择物品放入背包以使得总价值最大。
按照上述步骤,可以建立一个二维数组dp来记录每个子问题的最优解。最终得到的dp数组如下:
0 1 2 3 4 5 6 7 8 9 10
0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 3 3 3 3 3 3 3 3 3
2 0 0 3 4 4 7 7 7 7 7 7
3 0 0 3 4 5 7 8 9 9 12 12
4 0 0 3 4 5 7 8 9 9 12 12
5 0 0 3 4 5 7 8 9 9 12 13
可以看出,在背包容量为10时,最大总价值为13。通过回溯计算,可以确定选取了物品2、物品3和物品5。
完整 代码:
#include <iostream>
#include <vector>
using namespace std;
int knapsack_01(vector<int>& weights, vector<int>& values, int W) {
int n = weights.size();
vector<vector<int>> dp(n+1, vector<int>(W+1, 0));
for (int i = 1; i <= n; i++) {
for (int w = 1; w <= W; w++) {
if (weights[i-1] > w) {
dp[i][w] = dp[i-1][w];
} else {
dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1]);
}
}
}
return dp[n][W];
}
int main() {
vector<int> weights = {2, 3, 4, 5, 9};
vector<int> values = {3, 4, 5, 8, 10};
int W = 10;
int max_value = knapsack_01(weights, values, W);
cout << "Maximum value: " << max_value << endl;
return 0;
}