其他分享
首页 > 其他分享> > [LeetCode] 416. Partition Equal Subset Sum

[LeetCode] 416. Partition Equal Subset Sum

作者:互联网

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:

分割等和子集。

给你一个 只包含正整数 的 非空 数组 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 }

 

LeetCode 题目总结

标签:Subset,target,nums,int,max,Sum,Partition,sum,dp
来源: https://www.cnblogs.com/cnoodle/p/16419324.html