首页 > 编程语言 >多重背包问题 模板 C++实现

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

时间:2024-09-02 15:23:04浏览次数:12  
标签:cnt 背包 int C++ 模板 weight1 物品 dp

问题: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

    // 方法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 二进制优化
    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;
}


    

标签:cnt,背包,int,C++,模板,weight1,物品,dp
From: https://blog.csdn.net/2301_77329667/article/details/141816200

相关文章

  • 2024年华为OD机试E卷- Boss的收入-(Java&c++&Python)
    题目描述:一个XX产品行销总公司,只有一个b0ss,其有若千一级分销,一级分销又有若干二级分销,每个分错只有唯一的上级分销。规定,每个月,下级分销需要将自己的总收入(自已的+下级上交的)每满100元上交15元给自己的上级现给出一组分销的关系,和每个分销的收入,请找出boss并计算出这个boss......
  • C++中不定参数函数的实现方法
    目录一.前言二.代码解析1.模板方式第一种写法第二种写法2.va_list方式三.其他一.前言C++中实现不定参数函数可以采用多种方法,这篇文章讨论不定参数模板和va_list。二.代码解析1.模板方式C++中采用模板来实现不定参数列表,可以有多种编码的方法,这里提供两种方法。......
  • 对象池泛型模板
    对象池泛型模板delphi和lazarus都适用。泛型配合继承,无敌的存在。//cxg2024-9-2//对象池的泛型模板unitsys.pool;{$idef.inc}interfaceuses//system--------Generics.Collections,Classes,SysUtils;typeTPool<T>=classprivate//连接池的空闲......
  • c++ string 与 wstring 互转
    string转wstring:#include<iostream>#include<sstream>#include<locale>#include<string>#include<codecvt>intmain(){std::stringutf8_str="你好,世界!";std::wstring_convert<std::codecvt_utf8<wc......
  • 【C++】C++STL 揭秘:Strng背后的底层逻辑
    C++语法相关知识点可以通过点击以下链接进行学习一起加油!命名空间缺省参数与函数重载C++相关特性类和对象-上篇在上篇介绍string类的使用与理解,本篇将为大家来带关于string的底层实现逻辑,当然这不是一定库里面的实现逻辑。我们设计一个string类是为了在使用string类相关......
  • 命名空间在 C++ 中如何组织和管理代码?,c++中的命名空间是什么意思
    在C++编程中,命名空间(namespace)是组织和管理代码的重要工具。它为程序员提供了一种将代码按逻辑分组的方法,避免名称冲突,特别是在大型项目或使用多个库时显得尤为重要。命名空间可以看作是一个作用域,它包含了标识符(如变量、函数、类等)的集合。当我们在不同的模块中使用相同的标识符......
  • 【编程规范具体案例(基于Qt、微软、谷歌和AUTOSAR C++14 参考)】 C++ 编码规范 之程序设
    目录标题基本元素3.1类和结构体3.1.1\[必须]使用恰当的访问修饰符来管理类成员的可见性3.1.2\[必须]在类中合理使用默认的特殊成员函数3.1.3\[必须]提供清晰且尽可能一致的类接口3.1.4\[建议]优先使用初始化列表来初始化类成员3.1.5\[建议]使用抽......
  • 第3章_auto占位符(C++11~C++17)
    第3章auto占位符(C++11~C++17)3.1重新定义的auto关键字在C++11中静态成员变量是可以用auto声明并且初始化的,不过前提是auto必须使用const限定符。staticconstautox=5;遗憾的是,const限定符会导致x常量化,显然这不是我们想要的结果。在C++17标准中,对于静态成员变量,auto可以......
  • 第5章 函数返回类型后置(C++11)
    第5章函数返回类型后置(C++11)5.1使用函数返回类型后置声明函数语法:auto是一个占位符,int才是真正的返回类型autofoo()->int{return42;}返回一个函数指针类型,返回类型后置可能会是一个不错的选择intbar_impl(intx){returnx;}typedefint(*bar)(int);bar......
  • 第4章 decltype说明符(C++11~C++17)
    第4章decltype说明符(C++11~C++17)4.1回顾typeof和typeid(1)在C++11标准发布以前,GCC的扩展提供了一个名为typeof的运算符。通过该运算符可以获取操作数的具体类型。typeof是GCC所提供,并非C++标准。inta=9;typeof(a)b=5;(2)C++标准还提供了一个typeid运算符来获取与目标操......