【动态规划/背包问题】多重背包の二进制优化 |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 原题链接和其他优选题解。

相关文章
|
27天前
|
机器学习/深度学习 算法 安全
【PSO-LSTM】基于PSO优化LSTM网络的电力负荷预测(Python代码实现)
【PSO-LSTM】基于PSO优化LSTM网络的电力负荷预测(Python代码实现)
|
29天前
|
调度 Python
微电网两阶段鲁棒优化经济调度方法(Python代码实现)
微电网两阶段鲁棒优化经济调度方法(Python代码实现)
|
28天前
|
机器学习/深度学习 算法 调度
【EI复现】基于深度强化学习的微能源网能量管理与优化策略研究(Python代码实现)
【EI复现】基于深度强化学习的微能源网能量管理与优化策略研究(Python代码实现)
|
3月前
|
监控 大数据 API
Python 技术员实践指南:从项目落地到技术优化
本内容涵盖Python开发的实战项目、技术攻关与工程化实践,包括自动化脚本(日志分析系统)和Web后端(轻量化API服务)两大项目类型。通过使用正则表达式、Flask框架等技术,解决日志分析效率低与API服务性能优化等问题。同时深入探讨内存泄漏排查、CPU瓶颈优化,并提供团队协作规范与代码审查流程。延伸至AI、大数据及DevOps领域,如商品推荐系统、PySpark数据处理和Airflow任务编排,助力开发者全面提升从编码到架构的能力,积累高并发与大数据场景下的实战经验。
Python 技术员实践指南:从项目落地到技术优化
|
2月前
|
存储 监控 算法
基于 Python 跳表算法的局域网网络监控软件动态数据索引优化策略研究
局域网网络监控软件需高效处理终端行为数据,跳表作为一种基于概率平衡的动态数据结构,具备高效的插入、删除与查询性能(平均时间复杂度为O(log n)),适用于高频数据写入和随机查询场景。本文深入解析跳表原理,探讨其在局域网监控中的适配性,并提供基于Python的完整实现方案,优化终端会话管理,提升系统响应性能。
72 4
|
4月前
|
网络协议 API 开发者
分析http.client与requests在Python中的性能差异并优化。
合理地选择 `http.client`和 `requests`库以及在此基础上优化代码,可以帮助你的Python网络编程更加顺利,无论是在性能还是在易用性上。我们通常推荐使用 `requests`库,因为它的易用性。对于需要大量详细控制的任务,或者对性能有严格要求的情况,可以考虑使用 `http.client`库。同时,不断优化并管理员连接、设定合理超时和重试都是提高网络访问效率和稳定性的好方式。
121 19
|
2月前
|
数据采集 机器学习/深度学习 边缘计算
Python爬虫动态IP代理报错全解析:从问题定位到实战优化
本文详解爬虫代理设置常见报错场景及解决方案,涵盖IP失效、403封禁、性能瓶颈等问题,提供动态IP代理的12种核心处理方案及完整代码实现,助力提升爬虫系统稳定性。
154 0
|
6月前
|
机器学习/深度学习 算法 调度
【强化学习】基于深度强化学习的微能源网能量管理与优化策略研究【Python】
本项目基于深度Q网络(DQN)算法,通过学习预测负荷、可再生能源输出及分时电价等信息,实现微能源网的能量管理与优化。程序以能量总线模型为基础,结合强化学习理论,采用Python编写,注释清晰,复现效果佳。内容涵盖微能源网系统组成、Q学习算法原理及其实现,并提供训练奖励曲线、发电单元功率、电网交互功率和蓄电池调度等运行结果图表,便于对照文献学习与应用。
|
6月前
|
缓存 并行计算 数据处理
全面提升Python性能的十三种优化技巧
通过应用上述十三种优化技巧,开发者可以显著提高Python代码的执行效率和性能。每个技巧都针对特定的性能瓶颈进行优化,从内存管理到并行计算,再到使用高效的数值计算库。这些优化不仅能提升代码的运行速度,还能提高代码的可读性和可维护性。希望这些技巧能帮助开发者在实际项目中实现更高效的Python编程。
393 22
|
8月前
|
存储 缓存 Java
Python高性能编程:五种核心优化技术的原理与Python代码
Python在高性能应用场景中常因执行速度不及C、C++等编译型语言而受质疑,但通过合理利用标准库的优化特性,如`__slots__`机制、列表推导式、`@lru_cache`装饰器和生成器等,可以显著提升代码效率。本文详细介绍了这些实用的性能优化技术,帮助开发者在不牺牲代码质量的前提下提高程序性能。实验数据表明,这些优化方法能在内存使用和计算效率方面带来显著改进,适用于大规模数据处理、递归计算等场景。
218 5
Python高性能编程:五种核心优化技术的原理与Python代码

热门文章

最新文章

推荐镜像

更多