Last Stone Weight II

MediumDynamic Programming · 0/1 Knapsack (Subset Sum)🧩 Knapsack DPLeetCode #1049 ↗Updated

Problem

You have stones with positive weights stones[i]. In one move pick any two stones x ≤ y and smash them: equal stones both vanish, otherwise one stone of weight y - x remains. Continue until at most one stone is left. Return the smallest possible weight of that last stone (0 if none remain).
Inputstones = [2,7,4,1,8,1]Output1
Inputstones = [31,26,33,21,40]Output5

1 ≤ stones.length ≤ 30  ·  1 ≤ stones[i] ≤ 100

Try it yourself

Java
class Solution {
    public int lastStoneWeightII(int[] stones) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Any sequence of smashes ends with each original weight counted with a + or − sign, and every sign split is achievable. So split the stones into two piles as evenly as possible: find the largest subset sum best ≤ S/2, answer S - 2·best.
PatternKnapsack DP

Instinct Choose items to hit or stay under a target sum (can it be done, best value, fewest items, how many ways) - index a DP array by the remaining capacity.

Memory hack Once? Walk back. Many? Walk forward. Each item used at most once loops capacity downward; reusable items loop it upward.

Open the Knapsack DP guide →

Approaches

Brute ForceTry Every Sign Assignment

Time O(2n)Space O(n)

Give each stone a + or − sign (which pile it joins) and keep the smallest absolute total. This works because the final stone is exactly the difference between the two piles.

Java
public int lastStoneWeightII(int[] stones) {
    return explore(stones, 0, 0);
}

// smallest |diff| reachable after signing stones[i..]
private int explore(int[] stones, int i, int diff) {
    if (i == stones.length) return Math.abs(diff);
    int plus = explore(stones, i + 1, diff + stones[i]);
    int minus = explore(stones, i + 1, diff - stones[i]);
    return Math.min(plus, minus);
}

Simple once the sign insight is known, but 230 branches is too many.

At a glance

LevelApproachTimeSpace
Brute ForceTry Every Sign AssignmentO(2n)O(n)
BetterMemoized Recursion on (index, difference)O(n · S)O(n · S)
Optimal1-D Subset-Sum TableO(n · S)O(S)
Common pitfalls
  • Greedy smashing of the two heaviest stones (as in Last Stone Weight I) is not optimal here.
  • Scanning j upward in the 1-D table lets a stone be reused.
  • Forgetting to offset negative differences before using them as array indices.
Interview tips
  • Spend a minute justifying why every smash order equals a +/− sign assignment.
  • Then say: this is Partition Equal Subset Sum, but we want the closest-to-half sum.
  • Mention a BitSet shift-or as a constant-factor speedup.
Pattern
Subset sum / 0-1 knapsack → minimise the difference between two groups of a small-valued set.
My notes saved in this browser