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

【动态规划 | 二维费用背包问题】二维费用背包问题详解:状态设计与转移方程优化

在这里插入图片描述

算法相关知识点可以通过点击以下链接进行学习一起加油!
斐波那契数列模型路径问题 多状态问题子数组子序列
回文字串01背包完全背包两个数组匹配问题

在经典的背包问题中,我们通常只需考虑物品的体积或重量这一种限制条件。然而,在实际应用中,约束条件往往更加复杂,例如同时受到容量和承重的限制,这就是二维费用背包问题。如何高效设计状态表示,并优化转移方程,成为解决此类问题的关键。本文将详细分析二维费用背包的动态规划解法,探讨状态设计的技巧,并进一步优化转移过程,帮助读者掌握这一经典模型的扩展与应用。

请添加图片描述

Alt

🌈个人主页:是店小二呀
🌈C/C++专栏:C语言\ C++
🌈初/高阶数据结构专栏: 初阶数据结构\ 高阶数据结构
🌈Linux专栏: Linux
🌈算法专栏:算法
🌈Mysql专栏:Mysql

🌈你可知:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

    • 474. 一和零
    • 879. 盈利计划
    • 377. 组合总和 Ⅳ
    • 96. 不同的二叉搜索树

474. 一和零

题目】:474. 一和零

在这里插入图片描述

算法思路

在这里插入图片描述

初始化

在这里插入图片描述

当字符串为空时,其长度应初始化为 0。确保第一维的循环从小到大遍历,其余部分可按需处理。

代码实现

class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {int len = strs.size();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;for(auto ch : strs[i - 1])if(ch == '0') a++;else b++;//填表操作for(int j = 0; j <= m; j++){for(int k = 0; k <= n; k++){dp[i][j][k] = dp[i - 1][j][k];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];}
};

优化方案

class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {int len = strs.size();vector<vector<int>> dp(m + 1, vector<int>(n + 1));for(int i = 1; i <= len; i++){//统计字符个数int a = 0, b = 0;for(auto ch : strs[i - 1])if(ch == '0') a++;else b++;//填表操作for(int j = m; j >= a; j--){for(int k = n; k >= b; k--){dp[j][k] = max(dp[j][k], dp[j - a][k - b] + 1);}}}               return dp[m][n];}
};

既然需要判断才进入该语句,不然将判断添加到循环当中,因为为01背包问题,遍历方向是从右往左的。


879. 盈利计划

题目】:879. 盈利计划

在这里插入图片描述

算法思路

在这里插入图片描述

这道题从算法思路和题目分析角度来看都比较困难。通过对题目要求的深入分析,以及运用‘二维费用的动态规划经验’,我们可以得出合适的状态表示。在进行状态表示时,需要特别注意不超过:小于等于至少:大于等于这两者之间的区别与含义。

根据最后一个位置进行情况分析时,我们需要考虑选择的不同情况。如果选择了 dp[i - 1][j - g[i]][k - p[i]],我们首先需要分析 j - g[i] 是否可能小于零。如果 j - g[i] 小于零,意味着总人数 j 会小于当前使用的 g[i],这就违反了限制条件,因此这种情况是不合法的。

接下来,我们分析 k - p[i] 在状态转移方程中的作用,这是本题的难点之一。我们需要判断 k - p[i] 是否可能小于0。如果小于0,意味着当前的最低利润要求 k 小于当前选择的项目 p[i] 的利润,这种情况是允许的。但问题在于,数组下标不能为负数,因此我们需要确保 k - p[i] 不会导致负的数组下标。

在这里插入图片描述

为了解决这个问题,我们可以使用 max(0, k - p[i]),确保数组下标始终有效,并且为0作为最低限制。而在判断选择条件时,只需要保证 j >= g[i],即总人数 j 至少满足当前使用人数 g[i] 的要求。

“根据题目数据范围,需使用 MOD = 1e9 + 7 进行取模操作。在初始化方面,根据实际需求进行初始化。当任务和利润均为0时,人数是可变的,此时只有一种选择。而当没有任务时,利润为0,无论人数限制是多少,都能找到一个‘空集’方案。因此,我们将 dp[0][j][0] 初始化为 1,其中 0 <= j <= n

代码实现

class Solution {
public:int profitableSchemes(int n, int m, vector<int>& group, vector<int>& profit) {const int MOD = 1e9 + 7;int len =  group.size();vector<vector<vector<int>>> dp(len + 1, vector<vector<int>>(n + 1, vector<int>(m + 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 <= m; 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][m];}
};

优化方案

class Solution {
public:int profitableSchemes(int n, int m, vector<int>& group, vector<int>& profit) {const int MOD = 1e9 + 7;int len =  group.size();vector<vector<int>>dp (n + 1, vector<int>(m + 1));for(int j = 0; j <= n; j++) dp[j][0] = 1;for(int i = 1; i <= len; i++){for(int j = n ; j >= group[i - 1]; j--){for(int k = 0; k <= m; k++){dp[j][k] +=  dp[j - group[i - 1]][max(0, k - profit[i - 1])];dp[j][k] %= MOD;}}}return dp[n][m];}
};

377. 组合总和 Ⅳ

题目】:377. 组合总和 Ⅳ

在这里插入图片描述

算法思路

在这里插入图片描述

首先,需要明确背包问题本质上是一个组合问题,且在有限条件下求解。因此,分析题目及示例后,我们可以发现这道题求的是排列数,而非组合数,所以不能简单地使用背包问题的解法。

对于此类问题,状态表示的设计并不是凭借经验或题目分析就能轻易得到的,而是需要通过分析问题的过程中,发现重复的子问题并加以抽象,从而得到合适的状态表示。

以四个元素排列为例,可以设定状态为“凑成总和i,有多少种排列方式”。通过分析问题,结合“目标值 - 当前元素”的思路,最终得出状态转移方程。

在这里插入图片描述

初始化 dp[0] = 1 的目的是为了确保后续状态转移的正确性,但是强行解释为和0,也有一种方式。

代码实现

class Solution {
public:int combinationSum4(vector<int>& nums, int target) {vector<double> dp(target + 1);dp[0] = 1;for(int i = 1; i <= target; i++){for(auto x : nums){if(i >= x) dp[i] += dp[i - x];}}return dp[target];}
};

96. 不同的二叉搜索树

题目】:96. 不同的二叉搜索树

在这里插入图片描述

算法思路

在这里插入图片描述

这道题"经验 + 题目分析"很难得到状态表示,需要我们根据分析问题的过程中,发现重复子问题,抽象出来一个状态表示。

初始化 dp[0] = 1 的目的是为了确保后续状态转移的正确性,但是强行解释为和0,空树也是一种情况。

代码实现

class Solution {
public:int numTrees(int n) {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];}
};

在这里插入图片描述
快和小二一起踏上精彩的算法之旅!关注我,我们将一起破解算法奥秘,探索更多实用且有趣的知识,开启属于你的编程冒险!

http://www.dtcms.com/a/322130.html

相关文章:

  • 温室韭菜收割机的设计cad【12张】三维图+设计说明书
  • WinForm 实战 (进度条):用 ProgressBar+Timer 打造动态进度展示功能
  • AUTOSAR进阶图解==>AUTOSAR_EXP_AIOccupantAndPedestrianSafety
  • C++ Primer
  • window10本地运行datax与datax-web
  • 吴恩达 深度学习笔记
  • 校招秋招春招小米在线测评小米测评题库|测评解析和攻略|题库分享
  • 阶段二测试
  • 巧妙实现Ethercat转Profinet协议网关匹配光伏电站
  • 《P6464 [传智杯 #2 决赛] 传送门》
  • 五、CV_ResNet
  • Redis的Linux安装
  • python-操作mysql数据库(增删改查)
  • 医疗设备专用电源滤波器的安全设计与应用价值|深圳维爱普
  • Python接口测试实战之搭建自动化测试框架
  • 文学主题的演变
  • 智慧养老场景识别率↑91%!陌讯轻量化模型在独居监护的落地优化
  • 根据ASTM D4169-23e1标准,如何选择合适的流通周期进行测试?
  • 大语言模型的过去与未来——GPT-5发布小谈
  • 机器学习概念1
  • 数据库基础--多表关系,多表查询
  • 中小型企业ERP实施成本高?析客ERP系统独立部署+模块化配置的务实解决方案
  • ENET_GetRxFrame vs ENET_ReadFrame
  • MySQL 正则表达式详细说明
  • AI摄像头动捕:让动作分析更自由、更智能、更高效
  • 刚刚,GPT-5 炸裂登场!可免费使用
  • Word中怎样插入特殊符号
  • seo-使用nuxt定义页面标题和meta等信息
  • 11_Mybatis 是如何进行DO类和数据库字段的映射的?
  • HTTP/HTTPS代理,支持RSA和SM2算法