【动态规划/背包问题】多重背包の二进制优化 |Python 主题月

简介: 【动态规划/背包问题】多重背包の二进制优化 |Python 主题月

网络异常,图片无法展示
|


本文正在参加「Python主题月」,详情查看 [活动链接](https://juejin.cn/post/6979532761954533390/)
复制代码


回顾



上一讲 中我们说到,多重背包问题无法像完全背包那样,通过一维空间优化来降低时间复杂度。


同时,对多重背包中的物品进行「扁平化」,可以彻底转换成 01 背包问题。


但由于处理的物品没有变少,因此枚举的情况也不会变少,时间复杂度也不会发生改变,反而增加了扁平化的成本,使得算法的常数变大。


我们目前学的多重背包在各维度数量级同阶的情况下,时间复杂度为 ,只能解决 数量级的问题。


题目描述



有 种物品和一个容量为 的背包,每种物品「数量有限」。


第 件物品的体积是 ,价值是 ,数量为 。


问选择哪些物品,每件物品选择多少件,可使得总价值最大。


其实就是在 0-1 背包问题的基础上,增加了每件物品可以选择「有限次数」的特点(在容量允许的情况下)。


示例 1:


输入: N = 2, C = 5, v = [1,2], w = [1,2], s = [2,1]   
输出: 4
解释: 选两件物品 1,再选一件物品 2,可使价值最大。
复制代码


二进制优化



二进制优化可以使得我们能解决的多重背包问题数量级从 上升为 。


所谓的「二进制优化」其实是指,我们如何将一个多重背包问题彻底转化为 0-1 背包问题,同时降低其复杂度。


在上一节我们的扁平化方式是直接展开,一个数量为 的物品等效于 。


这样并没有减少运算量,但是如果我们能将 变成小于 个数,那么这样的「扁平化」就是有意义的。


学过 Linux 的都知道文件权限最高是 7,代表拥有读、写、执行的权限,但其实这个 7 是对应了 1、2、4 三个数字的,也就是 r:1w:2x:4 ,三种权限的组合共有 8 种可能性。


这里就采用了 3 个数来对应 8 种情况的“压缩”方法。


我们也可以借鉴这样的思路:将原本为 n 的物品用 ceil(log(n)) 个数来代替,从而降低算法复杂度。


7 可以用 1、2、4 来代替,像刚刚提到的 10 ,我们可以使用 1、2、4、3 来代替,你可能会有疑问,为什么是 1、2、4、3,而不是 1、2、4、6 或者 1、2、4、8 呢?


其实把他们几个数加起来就知道了,1、2、4、6 可以表达的范围是 013,而 1、2、4、8 可以表达的范围是 015,而我们要求的是表达 10,大于 10 的范围是不能被选择的。


所以我们可以在 1、2、4 (表达的范围是 07)的基础上,增加一个数 3(由 10 - 7 而来),这样就能满足我们需要表达的范围 010。


来看看通过「扁平化」来解决多重背包问题的代码。


Java 代码:


class Solution {
    public int maxValue(int N, int C, int[] s, int[] v, int[] w) {
        // 扁平化
        List<Integer> worth = new ArrayList<>();
        List<Integer> volume = new ArrayList<>();
        // 我们希望每件物品都进行扁平化,所以首先遍历所有的物品
        for (int i = 0; i < N; i++) {
            // 获取每件物品的出现次数
            int val = s[i];
            // 进行扁平化:如果一件物品规定的使用次数为 7 次,我们将其扁平化为三件物品:1*重量&1*价值、2*重量&2*价值、4*重量&4*价值
            // 三件物品都不选对应了我们使用该物品 0 次的情况、只选择第一件扁平物品对应使用该物品 1 次的情况、只选择第二件扁平物品对应使用该物品 2 次的情况,只选择第一件和第二件扁平物品对应了使用该物品 3 次的情况 ... 
            for (int k = 1; k <= val; k *= 2) { 
                val -= k;
                worth.add(w[i] * k);
                volume.add(v[i] * k);
            }
            if (val > 0) {
                worth.add(w[i] * val);
                volume.add(v[i] * val);
            }
        }
        // 0-1 背包问题解决方案
        int[] dp = new int[C + 1];
        for (int i = 0; i < worth.size(); i++) {
            for (int j = C; j >= volume.get(i); j--) {
                dp[j] = Math.max(dp[j], dp[j - volume.get(i)] + worth.get(i));
            }
        }
        return dp[C];
    }
}
复制代码


Python 3 代码:


class Solution:
    def maxValue(self, N, C, s, v, w):
        # 扁平化
        worth = []
        volume = []
        # 我们希望每件物品都进行扁平化,所以首先遍历所有的物品
        for i in range(N):
            # 获取每件物品的出现次数
            val = s[i]
            # 进行扁平化:如果一件物品规定的使用次数为 7 次,我们将其扁平化为三件物品:1*重量&1*价值、2*重量&2*价值、4*重量&4*价值
            # 三件物品都不选对应了我们使用该物品0次的情况、只选择第一件扁平物品对应使用该物品1次的情况、只选择第二件扁平物品对应使用该物品2次的情况,只选择第一件和第二件扁平物品对应了使用该物品3次的情况...
            k = 1
            while k <= val:
                val -= k
                worth.append(w[i] * k)
                volume.append(v[i] * k)
                k *= 2
            if val > 0:
                worth.append(w[i] * val)
                volume.append(v[i] * val)
        # 0-1 背包问题解决方案
        dp = [0] * (C + 1)
        for i in range(len(worth)):
            for j in range(C, volume[i] - 1, -1):
                dp[j] = max(dp[j], dp[j-volume[i]] + worth[i])
        return dp[C]
复制代码


总结



今天我们学习了「多重背包」的二进制优化。


与「直接将第 物品拆分为 件」的普通扁平化不同,二进制优化可以「将原本数量为 的物品拆分成 件」,使得我们转化为 01 背包后的物品数量下降了一个数量级。


经过「二进制优化」的多重背包单秒能解决的数据范围从 上升到 。


但其还不是「多重背包」问题优化的上限,下一讲我们将会介绍比「二进制优化」更优的优化方式。


最后



这是我们「刷穿 LeetCode」系列文章的第 No.* 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
3天前
|
数据采集 自然语言处理 算法
如何使用Python的Gensim库进行自然语言处理和主题建模?
使用Gensim库进行Python自然语言处理和主题建模,包括:1) 安装Gensim;2) 导入`corpora`, `models`, `nltk`等相关模块;3) 对文本数据进行预处理,如分词和去除停用词;4) 创建字典和语料库;5) 使用LDA算法训练模型;6) 查看每个主题的主要关键词。代码示例展示了从数据预处理到主题提取的完整流程。
38 3
|
3天前
|
存储 缓存 算法
优化Python代码性能的7个技巧
在日常的Python开发中,优化代码性能是一个重要的课题。本文介绍了7个实用的技巧,帮助开发者提高Python代码的执行效率,包括利用生成器表达式、使用适量的缓存、避免不必要的循环等。通过本文的指导,读者可以更好地理解Python代码性能优化的方法,提升自身的编程水平。
|
3天前
|
算法 Java 编译器
优化Python代码性能的实用技巧
提高Python代码性能是每个开发者的关注焦点之一。本文将介绍一些实用的技巧和方法,帮助开发者优化他们的Python代码,提升程序的执行效率和性能。
|
2天前
|
数据采集 数据可视化 数据挖掘
利用Python和Pandas库优化数据分析流程
在当今数据驱动的时代,数据分析已成为企业和个人决策的重要依据。Python作为一种强大且易于上手的编程语言,配合Pandas这一功能丰富的数据处理库,极大地简化了数据分析的流程。本文将探讨如何利用Python和Pandas库进行高效的数据清洗、转换、聚合以及可视化,从而优化数据分析的流程,提高数据分析的效率和准确性。
|
3天前
|
SQL 数据采集 数据挖掘
构建高效的Python数据处理流水线:使用Pandas和NumPy优化数据分析任务
在数据科学和分析领域,Python一直是最受欢迎的编程语言之一。本文将介绍如何通过使用Pandas和NumPy库构建高效的数据处理流水线,从而加速数据分析任务的执行。我们将讨论如何优化数据加载、清洗、转换和分析的过程,以及如何利用这些库中的强大功能来提高代码的性能和可维护性。
|
3天前
|
缓存 数据库连接 数据库
构建高性能的Python Web应用:优化技巧与最佳实践
本文探讨了如何通过优化技巧和最佳实践来构建高性能的Python Web应用。从代码优化到服务器配置,我们将深入研究提高Python Web应用性能的各个方面。通过本文,读者将了解到一系列提高Python Web应用性能的方法,从而更好地应对高并发和大流量的挑战。
|
3天前
|
缓存 并行计算 Serverless
优化Python代码性能的5个技巧
在日常Python编程中,代码性能的优化是一个重要的议题。本文介绍了5个实用的技巧,帮助你提高Python代码的执行效率,包括使用适当的数据结构、优化循环结构、利用内置函数、使用生成器表达式以及并行化处理。通过这些技巧,你可以更高效地编写Python代码,提升程序的性能和响应速度。
|
3天前
|
机器学习/深度学习 数据采集 数据可视化
Python众筹项目结果预测:优化后的随机森林分类器可视化|数据代码分享
Python众筹项目结果预测:优化后的随机森林分类器可视化|数据代码分享
|
3天前
|
机器学习/深度学习 算法 算法框架/工具
【Python机器学习专栏】深度学习中的正则化与优化技术
【4月更文挑战第30天】本文探讨了深度学习中的正则化和优化技术,以提升模型的泛化能力和训练效率。正则化包括L1和L2正则化以及Dropout,防止过拟合。优化技术涵盖梯度下降法、动量法和Adam优化器,加速模型收敛。Python示例展示了如何在Keras中应用这些技术,如L2正则化、Dropout及Adam优化器。
|
3天前
|
机器学习/深度学习 数据采集 算法
【Python机器学习专栏】自动化特征选择与优化的实践
【4月更文挑战第30天】特征选择在机器学习中至关重要,能降低模型复杂度,提高泛化能力和避免过拟合。本文介绍了自动化特征选择的三种方法:过滤法(如SelectKBest)、包装法(如RFE)和嵌入法(如随机森林)。通过结合这些方法,可实现特征优化,包括数据预处理、初步筛选、模型训练与评估、特征优化和结果验证。自动化特征选择能提升模型性能,适应不同数据集和任务需求,为机器学习项目提供坚实基础。