当前位置:   article > 正文

力扣:416. 分割等和子集_给你一个只包含正整数的非空数组

给你一个只包含正整数的非空数组

一、题目描述

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

示例 1:
输入: nums = [1,5,11,5]
输出: true
解释:数组可以分割成 [1, 5, 5][11]

示例 2:
输入: nums = [1,2,3,5]
输出: false
解释:数组不能分割成两个元素和相等的子集。

提示:
   1 <= nums.length <= 200
   1 <= nums[i] <= 100

二、问题分析

  先假设sum为数组的总和,需要确定是否可以将这个数组分割成两个元素和相等的子集,只要集合中出现总和sum/2的子集,就可以算这个数组可以分割成两个元素和相等的子集。也就是从数组从选出部分元素,使元素总和恰好为sum/2,推导到这里就可以用动态规划的经典理论0-1背包来解决问题。

  • 确定dp数组及下标的含义: d p [ i ] dp[i] dp[i]表示总容量是 i i i的背包最大可以凑成的总和
  • 确定递推公式:相当于在背包中放入数值,而且物品 i i i的重量是 n u m s [ i ] nums[i] nums[i],其价值也是 n u m s [ i ] nums[i] nums[i] d p [ i ] dp[i] dp[i]可以从 d p [ j − n u m s [ i ] ] + n u m s [ i ] dp[j-nums[i]]+nums[i] dp[jnums[i]]+nums[i],所以递推公式:dp[i]=max(dp[i],dp[j-nums[i]]+nums[i])
  • 初始化dp数组,题目中给出价值都是正整数,那么非0下标的数组都初始化为0即可。题目提示中说:每一个数组中的元素不会超过100,数组的长度不会超过200,那么总和不会大于20000,背包的最大容量只需要其中一半,所以10001就足够了。
  • 确定遍历顺序:0-1背包使用一维数组,遍历物品的for循环放在外层,遍历背包的for循环放在内层,且内层for循环使用倒叙遍历。

三、代码实现

// 编程软件:VS2019
// 参考书籍:代码随想录
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

// 时间复杂度O(n的平方),空间复杂度O(n)
bool canPartition(vector<int>& nums) {
	int sum = 0;
	vector<int> dp(10001, 0);
	// 计算数组总和
	for (int i = 0; i < nums.size(); i++) { 
		sum += nums[i];
	}
	// 如果数组总和为奇数,那么是不可能分成两个元素和相等的子集,直接返回false
	if (sum % 2 == 1) return false; 
	int target = sum / 2; 
	// 0-1背包逻辑
	for (int i = 0; i < nums.size(); i++) {
		// 每一个元素一定不能重复放入背包,所以for循环是从大到小遍历的
		for (int j = target; j >= nums[i]; j--) {
			dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
		}
	}
	// 判断是否恰好为target
	if (dp[target] == target) return true;
	return false;
}

int main() {
	vector<int>nums = { 1,5,11,5 };
	if (canPartition(nums))
		cout << "true";
	else
		cout << "false";
}
// 结果:true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

四、小结

  要学会从题目的要求出发,逆向的推导问题,将问题本质与学过知识联系。就像本题,从是否可将数组分割成两个元素和相等的子集出发,推导出从数组选取部分元素,而这些元素之和若恰好为 sum/2,则说明符合题目要求,这就与 0-1背包碰上了。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/744950
推荐阅读
相关标签
  

闽ICP备14008679号