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

力扣 416. 分割等和子集

题目来源:https://leetcode.cn/problems/partition-equal-subset-sum/description/

C++题解(思路来源代码随想录) :

背包问题有多种背包方式,常见的有:01背包、完全背包、多重背包、分组背包和混合背包等等。一个商品如果可以重复多次放入是完全背包,而只能放入一次是01背包,本题中是01背包。

把01背包问题套到本题上来。

  • 背包的体积为sum / 2
  • 背包要放入的商品(集合里的元素)重量为元素的数值,价值也为元素的数值
  • 背包如果正好装满,说明找到了总和为 sum / 2 的子集。
  • 背包中每一个元素是不可重复放入。

以上分析完,我们就可以套用01背包,来解决这个问题了。

  1. 确定dp数组以及下标的含义。二维数组: dp[i][j]表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
  2. 确定递推公式。两种情况:不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。);放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值。所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
  3. dp数组初始化。一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。状态转移方程可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化,即i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。当 j < weight[0]的时候,dp[0][j] 应该是 0;当j >= weight[0]时,dp[0][j] 应该是value[0]。
  4. 确定遍历顺序。有两个遍历的维度:物品与背包重量。都可以! 但是先遍历物品更好理解
  5. 举例推导dp数组。
class Solution {
public:bool canPartition(vector<int>& nums) {int len = nums.size();int sum = 0;for(int i = 0; i < len; i++) {sum += nums[i];}if(sum % 2 == 1) return false;vector<vector<int>> dp(len, vector<int>(sum/2+1, 0));for(int ii = nums[0]; ii <= sum/2; ii++) {dp[0][ii] = nums[0];}// 相当于包容量为sum/2,在len个物品中挑选,能装满则返回true。// 表示从0-j的元素中,取出和小于k的最大值。for(int j = 1; j < len; j++) {for(int k = 0; k <= sum/2; k++) {if(k < nums[j]) dp[j][k] = dp[j-1][k];else dp[j][k] = max(dp[j-1][k], dp[j-1][k-nums[j]]+nums[j]);}}if(dp[len-1][sum/2] == sum/2) return true;else return false;}
};

# 使用一维dp数组(滚动数组)

在使用二维数组的时候,递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);

与其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。

在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。

注意:遍历顺序必须先遍历物品再遍历包容量,且更新内层for循环需要递减(从后往前),因为滚动数组的更新需要用到未更新的前面元素,如果是递增(从前往后),前面更新的元素会影响后面的元素。

class Solution {
public:bool canPartition(vector<int>& nums) {int sum = 0;// dp[i]中的i表示背包内总和// 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200// 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了vector<int> dp(10001, 0);for (int i = 0; i < nums.size(); i++) {sum += nums[i];}// 也可以使用库函数一步求和// int sum = accumulate(nums.begin(), nums.end(), 0);if (sum % 2 == 1) return false;int target = sum / 2;// 开始 01背包for(int i = 0; i < nums.size(); i++) {for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);}}// 集合中的元素正好可以凑成总和targetif (dp[target] == target) return true;return false;}
};

http://www.lryc.cn/news/109074.html

相关文章:

  • sqlyog导出mysql数据字典
  • 【C++】多态的实现及其底层原理
  • 【网络编程】TCP带外数据总结
  • 高薪程序员面试题精讲系列133之微服务里的网关有哪些实现方案?你熟悉Gateway网关吗?
  • 计算机网络(4) --- 协议定制
  • 【Mybatis】Mybatis架构简介
  • 如何使用大模型处理生活繁琐的工作
  • RpcController作用浅析
  • Linux(三):Linux服务器下日常实操命令 (常年更新)
  • 强大的截图软件--Snipaste
  • LeetCode·每日一题·722. 删除注释·模拟
  • npm更新和管理已发布的包
  • 如何高效使用Gherkin
  • [CKA]考试之调度 pod 到指定节点
  • git 常用命令有哪些
  • 算法leetcode|66. 加一(rust重拳出击)
  • MySQL备份Shell脚本
  • Python批量查字典和爬取双语例句
  • uni-app、H5实现瀑布流效果封装,列可以自定义
  • vue echart3个饼图
  • LEARNING TO EXPLORE USING ACTIVE NEURAL SLAM 论文阅读
  • item_search-ks-根据关键词取商品列表
  • windows运行WPscan报错:无法打开库libcurl.dll
  • web前端框架Javascript之JavaScript 异步编程史
  • Java多线程(1)---多线程认识、四种创建方式以及线程状态
  • 搭建Django+pyhon+vue自动化测试平台
  • CASAIM自动化平面度检测设备3D扫描零部件形位公差尺寸测量
  • PostgreSql pg_ctl 命令
  • MySQL中的MVCC具体指的是什么?
  • Docker网络模型详解