【动态规划】背包问题 - 二维费用的01背包问题

news2024/11/25 15:02:55

文章目录

  • 1. 前言
  • 2. 二位费用的01背包问题·
    • 2.1_一和零
    • 2.2_盈利计划
    • 2.3_珠宝的最高价值
  • 3. 似包非包问题
    • 3.1_不同的二叉搜索树
    • 3.2_组合总和Ⅳ

1. 前言

关于 动态规划的理解 与例题,点击👇

【动态规划】C++解决斐波那契模型题目(三步问题、爬楼梯、解码方法…)

有了上面的经验,我们来解下面 二维费用的 01 背包问题

2. 二位费用的01背包问题·

2.1_一和零

在这里插入图片描述

思路

  1. 初始化数据

    • lenstrs 中字符串的数量。
    • dp[i][j][k] 表示在前 i 个字符串中,使用至多 j 个 ‘0’ 和 k 个 ‘1’ 能得到的最长子集长度。
  2. 统计每个字符串的 ‘0’ 和 ‘1’ 数量

    • 对于每个字符串,计算其包含的 ‘0’ 和 ‘1’ 数量,分别用 ab 表示。
  3. 动态规划状态转移

    • 不选择当前字符串,dp[i][j][k] = dp[i-1][j][k]
    • 如果可以选择当前字符串(即 j >= ak >= b),则更新状态为 dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-a][k-b] + 1),这表示选择当前字符串后的最长子集长度。
  4. 返回结果

    • 最终结果为 dp[len][m][n],即在所有字符串中,使用至多 m 个 ‘0’ 和 n 个 ‘1’ 能得到的最长子集长度。

这个算法通过三维 DP 表来处理每个子集的选择状态,确保在给定的 ‘0’ 和 ‘1’ 约束下,找到最大的子集长度。

代码

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        int len = strs.size();
        // 创建dp数组
        // dp[i][j][k]: 在前i个数中选,0的个数不超过j,1的个数不超过k,时的最长子集长度
        vector<vector<vector<int>>> dp(len+1, vector<vector<int>>(m+1, vector<int>(n+1)));

        for(int i = 1; i <= len; ++i)
        {
            int a = 0, b = 0; // 统计当前字符串的01个数
            for(char ch : strs[i-1]) // 下标映射
                if(ch == '1') b++;
                else a++;

            // 填表
            for(int j = 0; j <= m; ++j)
                for(int k = 0; k <= n; ++k)
                {
                    dp[i][j][k] = dp[i-1][j][k]; // 不选择i字符串
                    if(j >= a && k >= b)
                        dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-a][k-b]+1);
                }
        }

        return dp[len][m][n];
    }
};

2.2_盈利计划

在这里插入图片描述

思路

  1. 状态定义

    • dp[i][j][k] 表示使用前 i 个计划,选用的员工人数不超过 j,总利润至少为 k 的方案数量。
  2. 初始化

    • dp[0][j][0] = 1 表示在没有选择任何计划的情况下,选择任意人数的员工,总利润可以被认为是0的情况有且仅有1种:即什么都不做。
    • 对于其他情况下,当选择0个计划时,所有人数的情况下利润都不能满足大于0的条件,所以这些状态也应初始化为0。
  3. 状态转移

    • 对于每个计划 i,考虑是否选择该计划:
      • 如果选择该计划,更新 dp[i][j][k],将其设为 dp[i-1][j][k](不选择该计划的方案数)加上选择该计划后的方案数。
      • 选择该计划后,员工人数减少 group[i-1],利润减少 profit[i-1]。利润 k 需要用 max(0, k - profit[i-1]) 来避免负利润的情况。
    • 更新后的方案数需要对 MOD 取模,防止溢出。
  4. 结果

    • 最终的答案是 dp[len][n][minProfit],即使用所有计划,员工人数不超过 n,利润至少为 minProfit 的方案数量。

复杂度分析

  • 时间复杂度O(len * n * minProfit),其中 len 是计划数量,n 是员工数量,minProfit 是利润目标。
  • 空间复杂度O(len * n * minProfit),需要一个三维的 DP 数组来存储状态。

代码

class Solution {
public:
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        const int MOD = 1e9 + 7;
        int len = group.size();
        // 创建dp数组
        // dp[i][j][k]: 在前i个计划中选择,人数不超过j,利润至少为k 的选法数量
        vector<vector<vector<int>>> dp(len+1, vector<vector<int>>(n+1, vector<int>(minProfit+1)));
        // 初始化元素
        for(int j = 0; j <= n; ++j) dp[0][j][0] = 1;
        // 填表
        for(int i = 1; i <= len; ++i)
            for(int j = 0; j <= n; ++j)
                for(int k = 0; k <= minProfit; ++k)
                {
                    dp[i][j][k] = dp[i-1][j][k];
                    if(j >= group[i-1]) // 下标映射
                        dp[i][j][k] += dp[i-1][j-group[i-1]][max(0, k-profit[i-1])];
                    dp[i][j][k] %= MOD;
                } 

        return dp[len][n][minProfit];
    }
};

2.3_珠宝的最高价值

在这里插入图片描述

思路

  1. 初始化数据

    • n 是可用的总人数。
    • minProfit 是所需的最低利润。
    • groupprofit 分别表示每个计划所需的人数和带来的利润。
    • MOD 是取模的常量,避免结果溢出。
  2. 创建 DP 表

    • dp[i][j][k] 表示在前 i 个计划中,选择方案使得使用的人数不超过 j,并且获得的利润至少为 k 的选法数量。
  3. 初始化 DP 表

    • dp[0][j][0] 初始化为 1,表示在没有任何计划时,无论人数 j 是多少,总是有 1 种方式(即选择什么都不做)。
  4. 动态规划状态转移

    • 不选择当前计划,dp[i][j][k] = dp[i-1][j][k]
    • 如果选择当前计划(即 j >= group[i-1]),则从 dp[i-1][j-group[i-1]][max(0, k-profit[i-1])] 转移过来。这里的 max(0, k-profit[i-1]) 确保即使利润 k-profit[i-1] 小于零,也不会影响 DP 状态。
  5. 取模操作

    • 每次更新 dp 表时,都取模 MOD,确保结果不会因为大数而溢出。
  6. 返回结果

    • dp[len][n][minProfit] 表示在所有计划中,使用最多 n 人,并且获得至少 minProfit 利润的选法数量。

这段代码通过三维 DP 表来追踪不同计划、人数和利润条件下的方案数量,确保最终计算出符合条件的所有方案数。

代码

class Solution {
public:
    int jewelleryValue(vector<vector<int>>& frame) {
        int m = frame.size(), n = frame[0].size();
        // dp的创建 与 元素初始化
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));

        for(int i = 1; i <= m; ++i)
            for(int j = 1; j <= n; ++j)
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + frame[i-1][j-1]; // 映射下标(对于frame要-1)

        return dp[m][n];
    }
};

3. 似包非包问题


3.1_不同的二叉搜索树

在这里插入图片描述

思路

  1. 初始化 DP 数组

    • dp[i] 表示拥有 i 个节点的二叉搜索树的数量。
    • dp[0] 初始化为 1,表示空树(即没有节点)视为一种有效的 BST。
  2. 填充 DP 表

    • 使用两层循环:
      • 外层循环遍历所有节点数 i1n
      • 内层循环遍历每个节点 j 作为根节点时的情况,j1i
      • 对于每个 j 作为根节点,左子树有 j-1 个节点,右子树有 i-j 个节点。因此,dp[i] 的更新公式是 dp[i] += dp[j-1] * dp[i-j]
  3. 返回结果

    • dp[n] 就是拥有 n 个节点的所有可能的 BST 的数量。

代码

class Solution {
public:
    int numTrees(int n) {
        // 创建dp数组 dp[i]: 节点数为i时的二叉搜索树的种数
        vector<int> dp(n+1);
        // 初始化
        dp[0] = 1; // 空树算一种 / 为了后续填表正确
        // 填表
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= i; ++j)
                dp[i] += dp[j-1] * dp[i-j];

        return dp[n];
    }
};

3.2_组合总和Ⅳ

在这里插入图片描述

思路

  1. 初始化 DP 数组

    • vector<double> dp(target + 1); 创建了一个 DP 数组 dp,其中 dp[i] 表示和恰好为 i 的组合数。
    • dp[0] = 1; 初始化 dp[0]1,这是因为和为 0 的唯一组合是空组合。
  2. 动态规划填表

    • 遍历所有的 i1target
    • 对于每一个 i,遍历 nums 中的每个数 x
      • 如果 i >= x,那么 i 这个目标值可以由 i-x 这个目标值加上 x 得到。即,dp[i] 可以从 dp[i - x] 递推过来。
      • 因此,dp[i] += dp[i - x]; 更新 dp[i] 的值。
  3. 返回结果

    • return dp[target]; 最终返回 dp[target],即所有可能的组合数,使得选取的数的和为 target

代码

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        // 线性dp问题
        // 创建dp数组 dp[i]: 选择数使和恰好为i的组合总数
        vector<double> dp(target+1);
        dp[0] = 1; // 初始化
        for(int i = 1; i <= target; i++)
            for(int x : nums)
                if(i >= x)
                    dp[i] += dp[i - x];

        return dp[target];
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2083124.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

winXP下构建python开发环境

近期车间有个动平衡检测仪数采的需求&#xff0c;工控机是xp系统&#xff0c;原理也很简单&#xff0c;监控文件变化&#xff0c;发现有新的检测数据就调用远程接口传输到服务器上去。 通常python监控文件变化会用watchdog这个库&#xff0c; 可是xp太老了&#xff0c;测试了一…

身份实名认证-身份证实名认证-身份证实名-实名认证-身份证二要素-身份证实名认证-身份实名认证-身份证号码实名认证核验校验接口

身份证号码实名认证接口API是一种服务&#xff0c;它允许开发者或企业通过编程方式验证用户提供的身份证号码是否真实有效&#xff0c;以及该身份证号码与提供者的姓名是否匹配。这种服务对于确保用户身份的真实性、防止欺诈行为以及遵守相关法律法规&#xff08;如反洗钱法、网…

自博弈-PSRO类方法综述

参考文章&#xff1a;PSRO2024最新综述 关键名词 解释 Meta-Strategy Solver (MSS) 元博弈求解器&#xff0c;从现有策略集合中提取meta-strategy&#xff08;策略集合中每个策略对应一个权重&#xff09;用于构造新策略的优化目标 Response Objective&#xff08;RO&#…

【系统分析师】-缓存

目录 1、常见分类 2、集群切片方式 3、Redis 3.1、分布式存储方式 3.2、数据分片方式 3.3、数据类型 3.4、持久化方案 3.5、内存淘汰机制 3.6、Redis常见问题 4、布隆过滤器 1、常见分类 1、MemCache Memcache是一个高性能的分布式的内存对象缓存系统&#xff0c;用…

RocketMQ:高速消息中间件的秘密武器

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 关于RocketMQ的详细图表&#xff0c;包含了Producer、Consumer、Broker和NameServer等关键组件&#xff0c;展示…

你知道有哪些Spring MVC扩展点可以解析接口参数和处理返回值吗?

1.概述 Spring MVC 是一个灵活且强大的框架&#xff0c;它允许开发者在框架的基础上进行深度定制&#xff0c;以满足各种复杂的业务需求。HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler 是 Spring MVC 提供的两个重要扩展点&#xff0c;分别用于处理控制…

SLF4J 警告 - SLF4J: Class path contains multiple SLF4J bindings.

SLF4J 警告是因为类路径中存在多个 SLF4J 绑定。SLF4J 是一个抽象的日志接口&#xff0c;它可以与不同的日志实现&#xff08;如 Logback 或 SLF4J Simple&#xff09;一起使用。这个警告表明在你的项目中&#xff0c;SLF4J 找到了多个实现&#xff0c;导致它不知道该使用哪一个…

python如何判断回文

打开JUPTER NOTEBOOK&#xff0c;新建一个PYTHON文档。 n input("Please input string: ") print(n) 我们首先让用户输入要进行判断的字符串&#xff0c;然后打印出来查看一下。 n input("Please input string: ") is_palidrome n[::-1] if n is_palid…

Windows IPv6漏洞CVE-2024-38063

2024年8月&#xff0c;微软发现Windows10、Windows11、Windows Server2008~Server2022系统里&#xff0c;有个TCP/IP栈的远程代码执行漏洞&#xff0c;它通过目标系统的445端口&#xff0c;走IPv6协议&#xff0c;向目标系统发生特制的TCP包&#xff0c;执行任意代码&#xff0…

MySQL索引(三)

MySQL索引(三) 文章目录 MySQL索引(三)为什么建索引&#xff1f;怎么建立索引为什么不是说索引越多越好什么时候不用索引更好 索引怎么优化索引失效如何解决索引失效 学习网站&#xff1a;https://xiaolincoding.com/ 为什么建索引&#xff1f; 1.索引大大减少了MySQL需要扫描…

io进程中进程的创建,回收,退出

目录 一丶什么是进程 1.概念 2.特点 3 进程段 4.进程分类 5.进程状态 6.进程状态切换图 7.进程相关命令 8.优先级调度 二丶进程函数接口 1.创建进程fork() 2.进程回收wait() 3.结束进程exit() 4.获取进程号getpid(),getppid() 5.exec函数族 6.守护进程 特点&a…

AI辅助创作全攻略:如何高效利用人工智能撰写各类作品文字

在数字化时代的浪潮中人工智能&#xff08;AI&#xff09;已经渗透到咱们生活的方方面面&#xff0c;其中就包含文学创作领域。辅助创作不仅可以加强写作效率还能激发创作灵感宽创作视野。 那么怎么样高效利用人工智能撰写各类作品文字呢&#xff1f;本文将为您详细解析这一全攻…

软件测试 | 测试用例

测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境&#xff0c;测试步骤&#xff0c;测试数据&#xff0c;预期结果等要素。 设计测试用例原则⼀&#xff1a; 测试用例中⼀个必需部分是对…

进程间通信:采用有名管道,创建两个发送接收端,父进程写入管道1和管道2,子进程读取管道2和管道1.

作业1&#xff1a;有名管道&#xff0c;创建两个发送接收端&#xff0c;父进程写入管道1和管道2&#xff0c;子进程读取管道2和管道1. 右进程 #include <myhead.h> int main(int argc, const char *argv[]) {pid_t pidfork();if(pid>0)//父进程&#xff0c;将数据发…

PHP软件下载-安装-环境配置

.1.下载 下载地址如下 windows.php.net - /downloads/releases/ 安装包如下. .2.安装 可以在D盘或者E盘的根目录创建一个自定义目录。注意文件夹目录中不能包含中文&#xff0c;不能包含空格等特殊字符。 版本说明&#xff1a; (1)ts表示非线程安全版本。这个安装包还指明了…

easypoi实现ftl转doc文档(循环填充数据)

1.pom文件 java <dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId></dependency><!-- EasyPoi依赖 --><dependency><groupId>cn.afterturn</groupId><artifactId>easypo…

【生日视频制作】室内告白表白祝福布置霓虹灯AE模板修改文字软件生成器教程特效素材【AE模板】

室内告白表白祝福布置霓虹灯生日视频制作教程AE模板改字生成器 怎么如何做的【生日视频制作】室内告白表白祝福布置霓虹灯AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤&#xff1a; 安装AE软件下载AE模板把AE模板导入AE软件修改图片或文字渲染出视频

select epoll搭建并发式服务器

select 在C语言中&#xff0c;使用select函数可以创建一个并发式服务器。select是一个系统调用&#xff0c;它允许服务器同时监视多个文件描述符&#xff08;如套接字&#xff09;&#xff0c;以便知道哪个文件描述符准备好了进行读取或写入操作。这使得服务器能够同时处理多个…

Python简介、发展史

Python简介、发展史 本文目录&#xff1a; 零、时光宝盒 一、Python简介 二、Python设计者 三、Python发展史 四、Python语言的编程语言特性 五、Python现状 六、Python的未来 零、时光宝盒 我家所在的楼是3栋楼连接在一起的建筑&#xff0c;也就是3栋楼楼顶建筑上互通。…

学习笔记——后端项目中的相关技术 【随时更新】

文章目录 1. Session 共享1.0 cookie和session的工作流1.1 Cookie范围1.2 为什么要共享&#xff1f;1.3 如何共享存储1.4 session共享实现 1. Session 共享 1.0 cookie和session的工作流 在 Web 开发中&#xff0c;Cookie 和 Session 是非常常见的&#xff0c;尤其是在处理用…