[416]. 分割等和子集

Posted Debroon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[416]. 分割等和子集相关的知识,希望对你有一定的参考价值。

[416]. 分割等和子集

 


题目

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


算法设计:动态规划

背包问题的实质是,成本-收益问题,好像凡是要花钱的事儿,总应该问个性价比。

背包模型:

  • 变量:成本、收益
  • 连接关系:最优解、方案数、可行性、···

我对背包问题定义的理解:

  • 给定一个背包容量 target,再给定一个数组 nums (物品),能否按一定方式选取 nums 中的元素得到 target。

1、背包容量 target 和物品 nums 的类型可能是数,也可能是字符串

2、target可能题目已经给出(显式),也可能是需要我们从题目的信息中挖掘出来(非显式)

3、选取方式有常见的一下几种:每个元素选一次/每个元素选多次/选元素进行排列组

每个背包问题要求的也是不同的,按照所求问题分类,又可以分为以下几种:

1、最值问题:要求最大值/最小值

2、存在问题:是否存在…………,满足…………

3、组合问题:求所有满足……的排列组合

1、最值问题: dp[i] = max/min(dp[i], dp[i - nums] + 1) 或 dp[i] = max/min(dp[i], dp[i - num] + nums);
2、存在问题:dp[i] = dp[i] || dp[i - num];
3、组合问题:dp[i] += dp[i - num];

快来实践↘

像数学的题目都有很多隐藏信息,我们能想出更好的算法,就是比别人知道更多的信息。

转换思路:

  • 发现隐藏信息,对集合求和,得出隐藏信息 sum
  • 分割等和子集,得出隐藏信息 sum/2
  • 核心思路:是否可以从输入数组中挑选出一些正整数,使得这些数的和 等于 整个数组元素的和的一半 sum/2
  • 成本:物品中的体积,变成,数组里面的数
  • 收益:装满背包的最大收益,变成,恰好等于装满背包
  • 问题:有 N N N 种物品,每种物品都有体积 w i w_i wi每种物品仅有一件,可以选择放或不放,求解将哪些物品装入背包恰好装满。

设计状态:

  • dp[i][j]:对前 i 个物品,背包容量为 j 时,若 x 为 true,则恰好装满;否则不能恰好装满。

设计 base case:

  • dp[0][...] = false:没有物品,不能装满
  • dp[...][0] = false:背包没有容量,不能装满

设计最终状态:

  • dp[N][sum/2]

设计状态转移方程,最终状态从哪里来:

  • 不装第 N 种物品,dp[N][sum/2] = dp[N-1][sum/2],来自上一件物品的处理结果
  • 装入第 N 种物品,dp[N][sum/2] = dp[N-1][sum/2] || dp[N-1][j-nums[i]],如果装了第 i 种物品,就要看背包剩余容量 j-nums[i] 是否能恰好转满

因为 dp[i][j] 只从 dp[i-1][j]dp[i-1][j-w[i]] 而来,也就是说 [1....j] 这个数组是我们必须存的,但是 dp[1..i-1] 其实是不需要存的,只有 dp[i-1] 是有用的。

那可不可以设计一个一维数组 dp[1....j],来代表 dp[i-1][1.....j] 呢? 可以的

01 背包状态压缩:

bool canPartition(vector<int> &nums) 
    int sum = accumulate(nums.begin(), nums.end(), 0);
    if (sum % 2 == 1)              // 如果是和为奇数显然无法分成两个等和子集
        return false;
    int target = sum / 2; 
    vector<int> dp(target + 1, 0); // dp[i]: 是否存在子集和为i
    dp[0] = true;                  // 初始化:target=0 不需要选择任何元素,所以是可以实现的
    
    for (int num : nums)
        for (int i = target; i >= num; i--)
            dp[i] = dp[i] || dp[i - num];
            
    return dp[target];

以上是关于[416]. 分割等和子集的主要内容,如果未能解决你的问题,请参考以下文章

[416]. 分割等和子集

416-分割等和子集(01背包)

题目地址(416. 分割等和子集)

416分割等和子集

*Leetcode 416. 分割等和子集

leetcode 416. 分割等和子集