The smallest difference between two groups
Given positive integers nums, divide them into two groups (either may be empty) and return the smallest
possible absolute difference between the two groups' sums. (This is also the answer to "smash stones together; what is the
smallest weight that can be left?".)
Examples
| Input | Output | Why |
|---|---|---|
nums = [2,7,4,1,8,1] | 1 | For example {8, 2, 1} = 11 and {7, 4, 1} = 12. |
nums = [31,26,33,21,40] | 5 | {40, 33} = 73 and {31, 26, 21} = 78. |
nums = [1,6,11,5] | 1 | {1, 5, 6} = 12 and {11}. |
Constraints
- 1 ≤ nums.length ≤ 30
- 1 ≤ nums[i] ≤ 100
Hints
Hint 1
If one group sums to s, the difference is |total − 2s|.
Hint 2
Find every reachable subset sum up to total / 2 with the 0/1 knapsack.
Hint 3
The largest reachable s ≤ total / 2 gives the answer total − 2s.
Where people lose marks
- Simulating the stone smashing greedily (largest two first), which is not optimal.
- Searching sums above half the total, which double counts each split.
Dry run on [1,6,11,5]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 1reachT0T1·2·3·4T5T6T7·8·9·10T11
Total 23; reachable subset sums up to 11. The largest is 11, so the difference is 23 − 2 · 11 = 1.
How to think about it
Choosing the first group determines the second, and if the first sums to s the difference is total − 2s for
s at most half the total. So the best split puts in the first group the largest subset sum not exceeding total / 2. The
0/1 knapsack pattern computes every reachable subset sum up to that bound; scan down from total / 2 for the first
reachable one.
Complexity. Time O(n · total) · Space O(total)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def min_difference(nums):
total = sum(nums)
half = total // 2
reach = [True] + [False] * half
for x in nums:
for t in range(half, x - 1, -1): # 0/1: downwards
reach[t] = reach[t] or reach[t - x]
s = max(t for t in range(half + 1) if reach[t]) # the group closest to half
return total - 2 * s#include <numeric>
#include <vector>
int solve(const std::vector<int>& nums) {
int total = std::accumulate(nums.begin(), nums.end(), 0), half = total / 2;
std::vector<bool> reach(half + 1, false);
reach[0] = true;
for (int x : nums)
for (int t = half; t >= x; --t) reach[t] = reach[t] || reach[t - x];
int s = half;
while (!reach[s]) --s; // the reachable sum closest to half
return total - 2 * s;
}class Solution {
public int minDifference(int[] nums) {
int total = 0;
for (int x : nums) total += x;
int half = total / 2;
boolean[] reach = new boolean[half + 1];
reach[0] = true;
for (int x : nums)
for (int t = half; t >= x; t--) reach[t] = reach[t] || reach[t - x];
int s = half;
while (!reach[s]) s--; // the reachable sum closest to half
return total - 2 * s;
}
}function minDifference(nums) {
const total = nums.reduce((a, b) => a + b, 0), half = Math.floor(total / 2);
const reach = [true, ...new Array(half).fill(false)];
for (const x of nums) for (let t = half; t >= x; t--) reach[t] = reach[t] || reach[t - x];
return total - 2 * reach.lastIndexOf(true); // the reachable sum closest to half
}fn solve(nums: &[i64]) -> i64 {
let total: i64 = nums.iter().sum();
let half = (total / 2) as usize;
let mut reach = vec![false; half + 1];
reach[0] = true;
for &x in nums {
let x = x as usize;
if x > half {
continue;
}
for t in (x..=half).rev() {
reach[t] = reach[t] || reach[t - x];
}
}
let s = (0..=half).rev().find(|&t| reach[t]).unwrap() as i64; // closest to half
total - 2 * s
}import qualified Data.Set as S
solve :: [Int] -> Int
solve nums = total - 2 * S.findMax (foldl add (S.singleton 0) nums) -- the reachable sum closest to half
where
total = sum nums
half = total `div` 2
add reach x = S.union reach (S.fromList [t + x | t <- S.toList reach, t + x <= half])
Variants to try
- Groups must have equal sizes (add the count to the state).
- Return the two groups.
- Negative numbers allowed: shift totals by an offset.
Next problems
Can a subset reach the target?
Decide whether some subset of positive integers adds up to exactly the target. A boolean array over totals, filled downwards for each number.
Open the problem → Medium15 minSplit into two equal halves
Can the numbers be divided into two groups with the same sum? Only if half the total is reachable by some subset.
Open the problem → Easy10 minThe cheapest way up a staircase
Each step has a cost; climb one or two steps at a time; start on step 0 or 1. Return the cheapest way past the top.
Open the problem →Write a solution and run it against the real test table.
Keep going
Can a subset reach the target?
Decide whether some subset of positive integers adds up to exactly the target. A boolean array over totals, filled downwards for each number.
Open the problem → Medium15 minSplit into two equal halves
Can the numbers be divided into two groups with the same sum? Only if half the total is reachable by some subset.
Open the problem → Easy10 minThe cheapest way up a staircase
Each step has a cost; climb one or two steps at a time; start on step 0 or 1. Return the cheapest way past the top.
Open the problem → Dynamic programmingThe pattern behind it
Write the recursion, notice the repeats, fill a table in an order that makes each entry final.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.