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

动态规划-01背包问题

本篇中将介绍几道01背包类型的问题。

分割等和子集

416. 分割等和子集 - 力扣(LeetCode)

题目描述

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:

输入:nums = [1,5,11,5]

输出:true

解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5,2]

输出:false

解释:数组不能分割成两个元素和相等的子集。

 我们可以将这个问题视为一个典型的“背包问题”的变种,具体来说是“0-1背包问题”的一个特殊应用。不过,在这个问题中,我们不是要求达到某个特定重量的最大价值,而是要求判断是否存在一种方式,将数组中的元素分为两组,使得两组的和相等。

问题分析

  1. 目标和:首先,我们需要计算数组 nums 中所有元素的总和 totalSum。如果 totalSum 是奇数,那么显然无法将数组分割成两个和相等的子集,因为两个相等的和加起来必须是偶数。

  2. 动态规划数组:如果 totalSum 是偶数,我们定义 target = totalSum / 2。接下来,我们使用动态规划来判断是否存在一个子集的和为 target。我们定义一个布尔型动态规划数组 dp,其中 dp[i] 表示是否存在一个子集,其和为 i。注意,这里的 i 的取值范围是 0 到 target(包含)。

  3. 状态转移方程

    • 初始化:dp[0] = true(空集的和为0)。
    • 对于数组 nums 中的每个元素 num,以及每个可能的和 j(从 target 开始向下遍历到 num),如果 dp[j-num] 为真(即存在一个子集的和为 j-num),则我们可以将 num 加到这个子集中,使得子集的和变为 j,因此设置 dp[j] = true
  4. 结果判断:如果 dp[target] 为真,则说明存在一个子集的和为 target,即可以将数组 nums 分割成两个和相等的子集。

 

bool canPartition(vector<int>& nums) {  int totalSum = 0;  for (int num : nums) {  totalSum += num;  }  // 如果总和是奇数,则无法分割成两个和相等的子集  if (totalSum % 2 != 0) {  return false;  }  int target = totalSum / 2;  vector<bool> dp(target + 1, false);  dp[0] = true; // 空集的和为0  for (int num : nums) {  for (int j = target; j >= num; --j) {  if (dp[j - num]) {  dp[j] = true;  }  }  }  return dp[target];  
}  

目标和

494. 目标和 - 力扣(LeetCode)

题目描述

给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 。返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

例如,nums = [3, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+3-1" 以及"+3+1"等表达式。 

解题思路

 将数组分成两份一部分加上正号,另一部分加上负号,加正号的那一部分总和为a,另一部分总和绝对值为b

题目要求即要使a - b = target;

并且a + b = sum;

所以题目要求就是找出数组中 和为 : (target+sum)/2 的所有组合数。实际上就是01背包问题。

我们可以定义一个二维数组 dp[i][j] 来表示在前 i 个数字中选取一些数字(每个数字前面可以添加 '+' 或 '-'),使得这些数字的和为 j 的不同表达式的数目

int findTargetSumWays(vector<int>& nums, int target) {int n = nums.size();int sum = 0;for (int i = 0; i < n; i++) {sum += nums[i];}if ((target + sum) % 2 == 1)return false;int m = (target + sum) / 2;if (m < 0)return 0;vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));// dp[i][j]: 前i个数 总和为j 的所有方法数目for (int i = 0; i <= n; i++) { // 前i个数 什么都不选总和为0dp[i][0] = 1;}for (int i = 1; i <= n; i++) {for (int j = 0; j <= m; j++) {if (j - nums[i - 1] >= 0) {dp[i][j] = dp[i - 1][j - nums[i - 1]] + dp[i - 1][j];} else {dp[i][j] = dp[i - 1][j];}}}return dp[n][m];}


http://www.mrgr.cn/news/32857.html

相关文章:

  • EM算法讲解
  • 什么叫后验分布
  • C#基础(16)实践:学生成绩管理系统
  • 花朵识别系统Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
  • 算法工程师面试常考手撕题
  • 调整pycharm中的字体大小
  • 2024年信息学奥赛CSP-J1入门组初赛真题试卷
  • 2024年华为杯数学建模研赛(F题) 建模解析| 卫星轨道 | 小鹿学长带队指引全代码文章与思路
  • Java String indexOf()方法
  • 论文推荐——犹豫直觉模糊偏好关系积性一致性及其在群决策中的应用
  • 【远程调用PythonAPI-flask】
  • 滑动窗口算法专题(1)
  • 【更新】上市公司绿色专利申请及授权数据(2000-2023年)
  • 独立站如何批量查收录,如何进行独立站的批量收录查询的详细操作
  • SpringBoot 整合 Caffeine 实现本地缓存
  • 【快手】前端校招一面
  • 自然语言处理-基于注意力机制的文本匹配
  • 并查集LRU cache
  • 【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】010 - 二号内核线程 kthreadd线程 工作流程分析
  • 前端入门:HTML+CSS简便开发的技巧