标签:Subset target nums int max Sum Partition sum dp
Given a non-empty array nums
containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
Example 1:
Input: nums = [1,5,11,5] Output: true Explanation: The array can be partitioned as [1, 5, 5] and [11].
Example 2:
Input: nums = [1,2,3,5] Output: false Explanation: The array cannot be partitioned into equal sum subsets.
Constraints:
1 <= nums.length <= 200
1 <= nums[i] <= 100
分割等和子集。
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/partition-equal-subset-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这是一道非常好的动态规划/0-1背包问题。为什么是0-1背包是因为每个数字只能用一次,只能选择用或不用。
首先我们来理解一下题意,题目给了一个非空的正整数数组(nums[i] > 0),请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。这个问题我们可以把他转化为我们能不能从中挑出一些数字,使得这些数字的和正好为所有数字的和的一半。
这里我们创建一个二维的DP数组,定义是从前 i 个数字内选取若干个正整数,是否存在一种方案使得他们的和等于 j。其他讲解可以直接参见代码注释。
时间O(mn)
空间O(mn)
Java实现
1 class Solution { 2 public boolean canPartition(int[] nums) { 3 int len = nums.length; 4 int sum = 0; 5 int max = 0; 6 for (int num : nums) { 7 sum += num; 8 max = Math.max(max, num); 9 } 10 int target = sum / 2; 11 12 // corner case 13 if (sum % 2 == 1) { 14 return false; 15 } 16 if (max > target) { 17 return false; 18 } 19 20 // normal case 21 // dp定义是从数组的前 i 个数内选取若干个正整数,是否存在一种方案使得他们的和等于 j 22 boolean[][] dp = new boolean[len][target + 1]; 23 // 因为input都是正数,所以无法使得他们的和等于0 24 dp[0][0] = false; 25 // 任何方案,只要不选,都可以使他们的和为0 26 for (int i = 0; i < len; i++) { 27 dp[i][0] = true; 28 } 29 // 选了第一个数字,和为nums[0] 30 dp[0][nums[0]] = true; 31 for (int i = 1; i < len; i++) { 32 for (int j = 1; j < target + 1; j++) { 33 // 不考虑当前数字 34 dp[i][j] = dp[i - 1][j]; 35 if (j >= nums[i]) { 36 dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]; 37 } 38 } 39 // 剪枝 40 if (dp[i][target] == true) { 41 return true; 42 } 43 } 44 return dp[len - 1][target]; 45 } 46 }
我们注意到DP数组的更新其实每次都只跟上一行的DP值有关,所以我们这里考虑把二维数组降成一维。注意27行为什么 j 指针是从右往左扫描,因为这样我们就不会覆盖掉之前记录的DP值了。
时间O(mn)
空间O(n)
Java实现
1 class Solution { 2 public boolean canPartition(int[] nums) { 3 int len = nums.length; 4 int sum = 0; 5 int max = 0; 6 for (int num : nums) { 7 sum += num; 8 max = Math.max(max, num); 9 } 10 int target = sum / 2; 11 12 // corner case 13 if (sum % 2 == 1) { 14 return false; 15 } 16 if (max > target) { 17 return false; 18 } 19 20 // normal case 21 boolean[] dp = new boolean[target + 1]; 22 dp[0] = true; 23 if (nums[0] <= target) { 24 dp[nums[0]] = true; 25 } 26 for (int i = 1; i < nums.length; i++) { 27 for (int j = target; j >= nums[i]; j--) { 28 if (dp[target]) { 29 return true; 30 } 31 dp[j] = dp[j] || dp[j - nums[i]]; 32 } 33 } 34 return dp[target]; 35 } 36 }
标签:Subset,target,nums,int,max,Sum,Partition,sum,dp 来源: https://www.cnblogs.com/cnoodle/p/16419324.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。