多重背包问题
一、问题定义
二、与其他背包问题的区别
三、朴素解法
直接遍历每种物品的所有可能选取数量(从 0 到最大限制数),通过动态规划更新最大价值。虽然时间复杂度较高(适合物品数量限制较小的场景),但逻辑简单易懂。
【参考代码】
#include <iostream> #include <algorithm> using namespace std; // 多重背包问题(朴素解法) // 参数: // - weight:物品重量数组 // - value:物品价值数组 // - count:物品数量限制数组 // - n:物品种类数 // - W:背包最大承重 int boundedKnapsackNaive(int weight[], int value[], int count[], int n, int W) { // dp[j]:承重为j时的最大价值 int dp[1001] = {0}; // 假设背包最大承重不超过1000 // 遍历每种物品 for (int i = 0; i < n; i++) { // 逆向遍历承重(避免同一物品被重复选取多次,类似0-1背包的遍历逻辑) for (int j = W; j >= weight[i]; j--) { // 遍历当前物品的所有可能选取数量(k=0到最大可能数量) // 最大可能数量:min(count[i], j / weight[i])(不超过数量限制,且不超重) for (int k = 1; k <= count[i] && k * weight[i] <= j; k++) { // 更新dp[j]:选取k个当前物品的最大价值 dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]); } } } return dp[W]; } int main() { // 示例:3种物品,背包最大承重为10 int weight[] = {2, 3, 4}; // 物品重量 int value[] = {3, 5, 6}; // 物品价值 int count[] = {2, 2, 1}; // 物品数量限制(最多选2、2、1次) int n = 3; // 物品种类数 int W = 10; // 背包最大承重 int maxVal = boundedKnapsackNaive(weight, value, count, n, W); cout << "多重背包(朴素解法)的最大价值为:" << maxVal << endl; // 输出:16 return 0; }1. 核心逻辑
2.示例解析
3. 时间复杂度
四、动态规划解法(二进制优化)
1. 二进制拆分原理
2. 算法步骤
3. 参考代码
#include <iostream> #include <algorithm> using namespace std; // 最大虚拟物品数量(根据实际场景调整,确保足够存储拆分后的物品) #define MAX_VIRTUAL_ITEMS 1000 // 多重背包问题(二进制优化,不使用vector) // 参数: // - weight:物品重量数组 // - value:物品价值数组 // - count:物品数量限制数组 // - n:物品种类数 // - W:背包最大承重 int boundedKnapsack(int weight[], int value[], int count[], int n, int W) { // 步骤1:二进制拆分,生成虚拟物品(用普通数组存储) int new_weight[MAX_VIRTUAL_ITEMS]; // 拆分后的虚拟物品重量 int new_value[MAX_VIRTUAL_ITEMS]; // 拆分后的虚拟物品价值 int virtual_count = 0; // 虚拟物品的总数量 for (int i = 0; i < n; i++) { int cnt = count[i]; // 当前物品的数量限制 // 二进制拆分:2^0, 2^1, ..., 2^k(直到剩余数量为0) for (int k = 1; cnt > 0; k *= 2) { int take = min(k, cnt); // 本次拆分的数量(不超过剩余数量) // 存储虚拟物品(重量和价值为原物品的take倍) new_weight[virtual_count] = weight[i] * take; new_value[virtual_count] = value[i] * take; virtual_count++; // 虚拟物品数量+1 cnt -= take; // 剩余数量减少 } } // 步骤2:用0-1背包解法处理虚拟物品(逆向遍历承重) int dp[1001] = {0}; // dp[j]:承重为j时的最大价值(假设W<=1000) for (int i = 0; i < virtual_count; i++) { // 逆向遍历,防止同一虚拟物品被多次选取(0-1背包特性) for (int j = W; j >= new_weight[i]; j--) { dp[j] = max(dp[j], dp[j - new_weight[i]] + new_value[i]); } } return dp[W]; } int main() { // 示例:3种物品,背包最大承重为10 int weight[] = {2, 3, 4}; // 物品重量 int value[] = {3, 5, 6}; // 物品价值 int count[] = {2, 2, 1}; // 物品数量限制(最多选2、2、1次) int n = 3; // 物品种类数 int W = 10; // 背包最大承重 int maxVal = boundedKnapsack(weight, value, count, n, W); cout << "多重背包的最大价值为:" << maxVal << endl; // 输出:14 return 0; }