问题
分析
道题是典型的01背包问题,目的是在保证装入物品不超过背包容量的前提下,尽可能的多装入一些物品,使得背包内的物品价值最大。
在解决问题之前,为描述方便,首先定义一些变量:Vi表示第 i 个物品的价值,Wi表示第 i 个物品的体积,定义V(i,j):当前背包容量 j,前 i 个物品最佳组合对应的价值。
下面寻找递推关系式,面对当前商品有两种可能性:
1.包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的,即V(i,j)=V(i-1,j);
2.还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个,即V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}。其中V(i-1,j)表示不装,V(i-1,j-w(i))+v(i) 表示装了第i个商品,背包容量减少w(i),但价值增加了v(i)。
由此可以得出递推关系式:
j<w(i) V(i,j)=V(i-1,j)
j>=w(i) V(i,j)=max{V(i-1,j),V(i-1,j-w(i))+v(i)}
之后就是两层for循环利用递推关系式填dp二维数组的表了
代码和运行结果
#include <iostream>
#include <vector>
// 定义物品结构体
struct Item {
int weight;
int value;
};
// 获取最大值
int max(int a, int b) {
return (a > b) ? a : b;
}
// 背包问题函数
int knapsack(int capacity, const std::vector<Item>& items) {
int n = items.size();
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(capacity + 1, 0));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= capacity; ++j) {
if (items[i - 1].weight <= j) {
dp[i][j] = max(dp[i - 1][j], items[i - 1].value + dp[i - 1][j - items[i - 1].weight]);
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
// 根据 dp 数组回溯得到选择方案
std::vector<int> chosenItems(n, 0);
int i = n, j = capacity;
while (i > 0 && j > 0) {
if (dp[i][j] != dp[i - 1][j]) {
chosenItems[i - 1] = 1;
j -= items[i - 1].weight;
}
--i;
}
// 输出选择方案
for (int k = 0; k < n; ++k) {
std::cout << chosenItems[k];
}
std::cout << std::endl;
return dp[n][capacity];
}
int main() {
int n;
std::cin >> n;
std::vector<Item> items(n);
for (int i = 0; i < n; ++i) {
std::cin >> items[i].weight >> items[i].value;
}
int capacity;
std::cin >> capacity;
int maxTotalValue = knapsack(capacity, items);
return 0;
}
01背包问题的核心就是在寻找递推关系式,寻找递推关系式的思路如下:
如果当前物品的重量Wi大于背包的容量j,则无法将该物品放入背包中,因此当前最佳组合的总价值与前i-1个物品的最佳组合总价值相同,即V(i, j) = V(i-1, j)。
如果当前物品的重量Wi小于等于背包的容量j,那么可以选择将该物品放入背包,也可以选择不放入背包,取两种情况下的总价值的较大值,即V(i, j) = max{V(i-1, j), V(i-1, j-Wi) + Vi}。其中,V(i-1, j)表示不放入当前物品时的最佳组合总价值,V(i-1, j-Wi) + Vi表示放入当前物品后的最佳组合总价值,其中背包容量减少Wi,但总价值增加Vi。
根据递推关系式,我们可以使用二维数组dp来记录子问题的最优解。通过两层循环遍历每个物品和背包容量,填充dp数组中的值,最终得到dp[n][C]即为所求的最大总价值,其中n为物品的个数,C为背包的容量。