当前位置: 首页 > news >正文

多重背包问题 模板 C++实现

问题:n 种物品和一个容量是 的背包。

i 种物品最多有 num [ i - 1 ] 件,每件体积是 weight [ i - 1] ,价值是 value [ i - 1 ]

求解将哪些物品装入背包,可使物品重量总和不超过背包容量,且价值总和最大。输出最大价值。


算法1:三重循环

内层循环用于考虑当前物品 i 可以被选择的数量,k 代表选择当前物品的数量。

与完全背包相比,多重背包问题只多了 不可超过最大可选第 i 件物品数量 nums [ i - 1 ] 这一个限制,加上即可。

循环的条件 k <= num [ i - 1 ] && k * weight [ i ] <= j 确保了两个限制:不超过物品的最大可选数量 num [ i - 1 ] ,以及所选物品的总体积 k * weight [i - 1] 不超过当前背包容量 j 。

此方法的 时间复杂度为:O(n³)

代码:

// 方法1
int dp[4+1][20+1];
memset(dp, 0, sizeof(dp));// 初始化dp(方法1)
for (int i = 1; i <= n; ++i)for (int j = 0; j <= c; ++j)for (int k = 0; k <= num[i - 1] && k * weight[i - 1] <= j; k++)dp[i][j] = max(dp[i][j], dp[i - 1][j - weight[i - 1] * k] + value[i - 1] * k);
cout << dp[n][c] << endl;

算法2:

为了降低时间复杂度,我们要把 多重背包问题 转换成 0/1背包问题

如上图所示,这样简单拆分,只是把 O(n³) 的时间复杂度变成了 O(n²) ,但 n 变多了,总体运行次数没有变化。那么我们是否存在一种方式,我们不需要枚举 n i 物品,就能够表示 ni 物品呢?

我们的十进制数可以用二进制数来表示。二进制 0 1 2 4 ... ,可以表达从 0 7 的数字,同样的,假设一共有 7 件 a 物品,我们可以把他分为 1,2,4 的组合,把他分为 3 件物品,这样的话如果我们想装入 3a 物品,只需要把 1 件和 2 件装入即可。除此之外,要创建新的 weight1 value1 数组,用来存储重新分组的值。

那么我们发现,此时我们利用 O(logn) 级别的数字表示了 O(n) 。时间上做了非常大的优化。而这种优化方式被称作 二进制优化

代码:

int dp[20 + 1] = { 0 }, cnt = 0, weight1[20] = { 0 }, value1[20] = { 0 };
for (int i = 0; i < n; i++){int k = 1, w = weight[i], v = value[i], m = num[i];// 进行 “打包” 转换:二进制优化,转换成01背包while (k < m){weight1[cnt] = k * w, value1[cnt++] = k * v;m -= k;k *= 2;}if (m > 0)  weight1[cnt] = m * w, value1[cnt++] = m * v;// 剩余的分一组
}// 利用01背包中的空间优化模板求解。for (int i = 0; i < cnt; i++)for (int j = c; j >= weight1[i]; j--)dp[j] = max(dp[j], dp[j - weight1[i]] + value1[i]);
cout << dp[c] << endl;

完整代码:

#include<iostream>using namespace std;int main() {// weight重量 value价值 num每件物品的个数int weight[4] = { 3,5,2,8 }, value[4] = { 4,6,1,9 }, num[4] = { 3, 4, 6, 1 };  int n = 4, c = 20;// 物品个数n,背包容量c// 方法1int dp[4+1][20+1];memset(dp, 0, sizeof(dp));// 初始化dp(方法1)for (int i = 1; i <= n; ++i)for (int j = 0; j <= c; ++j)for (int k = 0; k <= num[i - 1] && k * weight[i - 1] <= j; k++)dp[i][j] = max(dp[i][j], dp[i - 1][j - weight[i - 1] * k] + value[i - 1] * k);cout << dp[n][c] << endl;/*// 方法2 二进制优化int dp[20 + 1] = { 0 }, cnt = 0, weight1[20] = { 0 }, value1[20] = { 0 };for (int i = 0; i < n; i++){int k = 1, w = weight[i], v = value[i], m = num[i];// 进行 “打包” 转换:二进制优化,转换成01背包while (k < m){weight1[cnt] = k * w, value1[cnt++] = k * v;m -= k;k *= 2;}if (m > 0)  weight1[cnt] = m * w, value1[cnt++] = m * v;// 剩余的分一组}// 利用01背包中的空间优化模板求解。for (int i = 0; i < cnt; i++)for (int j = c; j >= weight1[i]; j--)dp[j] = max(dp[j], dp[j - weight1[i]] + value1[i]);cout << dp[c] << endl;
*/return 0;
}


http://www.mrgr.cn/news/17965.html

相关文章:

  • 关于contextmenu-ui组件库
  • 【Python123题库】#统计文章字符数 #查询高校信息 #查询高校名
  • IM项目:进阶版即时通讯项目---项目总览
  • Trino大量查询会导致HDFS namenode主备频繁切换吗?
  • LRU Cache
  • 5.12 飞行控制——PID参数优化
  • Oracle手动误删物理上的数据文件解决办法
  • 多头切片的关键:Model 类 call解释;LlamaModel 类 call解释;多头切片的关键:cache的数据拼接
  • three.js 开发粒子系统
  • RK3568平台(内存篇)Linux内存管理
  • 如何判断请求是否为跨域请求?——详细教程
  • 【负载均衡】
  • 【安卓13】解决HDMI OUT和耳机等设备接入时会解除静音问题
  • 大数据测试知识架构与技术框架分享|大数据测试工程师学习方向
  • 这一届“出道”的数字人,已经拿捧上了“铁饭碗”
  • 如何读懂以太坊源代码
  • C++刷题之二:vector迭代器的使用
  • springboot 配置ssl支持https
  • 43款最新泛微Ecology9精品应用(一键导入,轻松上手)
  • Axure健康助理小程序原型图70+页,医疗类高保真高交互模板