Problem overview
The method canPartition receives an int[] named nums of positive integers and returns true when its numbers can be split into two groups with equal sums, and false otherwise. Every number goes into exactly one group, and repeated values are separate numbers, each copy going into one group.
For [1, 5, 11, 5] the answer is true: [1, 5, 5] and [11] both add up to 11. For [1, 2, 3, 5] it is false, since the total, 11, is odd. For [1, 2, 5] it is also false: the total is 8, each group would need 4, but the numbers can only make 1, 2, 3, 5, 6, 7 and 8.
Approach: can some of the numbers make half the total?
If the total is odd, two equal halves are impossible. Otherwise, let half be the total divided by 2. If some of the numbers add up to exactly half, the remaining numbers add up to the other half. So the question becomes: can a selection of numbers, each used at most once, add up to half?
Trying every selection is hopeless: 200 numbers can be grouped in 2^200 ways. Dynamic programming keeps a table of booleans instead. can[s] says whether some of the numbers seen so far can add up to s, for every s from 0 to half. Before any number is seen, only can[0] is true: no numbers at all make 0.
The numbers are added one at a time. For a number num, every sum that could be made before gives a new sum with num added, so can[s] becomes true when can[s - num] is. Here is how can grows for [1, 5, 11, 5], with half = 11, listing the sums that are true:
after 1: 0 1
after 5: 0 1 5 6
after 11: 0 1 5 6 11
after 5: 0 1 5 6 10 11
After the last number, 11 is in the list, so the answer is true.
Why the inner loop goes down
The loop over sums runs from half down to num. Going down, can[s - num] has not been changed yet for this number, so it still describes the numbers before it, and each number is used at most once. Going up would let a number build on its own earlier update and be used again and again. That is the right behavior for a problem where a coin can be used many times, but wrong here. The run shows it: with an upward loop, [1, 2, 5] gives true, as if 4 were 1 + 1 + 1 + 1 or 2 + 2, while the downward loop gives false.
Step by step
- Sum the numbers into
totalwithArrays.stream(nums).sum(). The stream turns the array into a sequence of its numbers, andsum()adds them. - Return
falseiftotal % 2 == 1. Two equal halves always give an even total. - Store
total / 2inhalf. This is the target for one group. - Create
can, aboolean[]of sizehalf + 1. New boolean arrays start allfalse. - Set
can[0] = true. The empty selection makes 0, and every other sum grows from it. - Loop over the numbers with an enhanced
for. After a number is handled,canknows every sum the numbers so far can make. - Loop over the sums from
halfdown tonum. A sum smaller thannumcannot contain it. - Update with
can[s] = can[s] || can[s - num]. If some earlier numbers makes - num, addingnummakess. - Return
can[half].
The solution in Java
class Solution {
public boolean canPartition(int[] nums) {
int total = Arrays.stream(nums).sum();
if (total % 2 == 1) {
return false;
}
int half = total / 2;
boolean[] can = new boolean[half + 1];
can[0] = true;
for (int num : nums) {
for (int s = half; s >= num; s--) {
can[s] = can[s] || can[s - num];
}
}
return can[half];
}
}
Java details: boolean[] is initialized to false, which is what the table needs. Arrays.stream(nums) on an int[] gives an IntStream, whose sum() returns an int. With at most 200 numbers of at most 100 the total stays far below the int limit, so overflow is not a concern. In can[s] || can[s - num], the || short-circuits, but both operands are plain array reads, so the order has no side effects. The inner loop's condition s >= num also guarantees that s - num is never negative.
Complexity
Each of the n numbers goes over at most half + 1 sums, with constant work for each, so the time is O(n · sum), where sum is the total. The table holds half + 1 booleans, so the extra space is O(sum). For 200 numbers equal to 100, the total is 20000 and the method still answers true quickly.
Edge cases and common mistakes
- Odd total.
[2]and[1, 2, 3, 5]returnfalseimmediately. - A number larger than
half. The inner loop does not run for it, becausehalf >= numis false, and the answer comes from the other numbers. For[100, 100, 100, 100, 99]the total is odd, so the answer isfalse. - Looping up instead of down. Numbers get reused, and
[1, 2, 5]wrongly givestrue. - Forgetting
can[0] = true. Then nothing can ever become true, and every answer isfalse. - Checking only for an even total. An even total is necessary, not sufficient, as
[1, 2, 5]shows. - Table of size
half. Indexhalfwould be out of range; the table needshalf + 1entries.
Testing
These inputs were run against the solution above:
| nums | Result |
|---|---|
[1, 5, 11, 5] | true |
[1, 2, 3, 5] | false |
[1, 2, 5] | false |
[3, 3] | true |
[2] | false |
[1, 1] | true |
[100, 100, 100, 100, 99] | false |
