Browse curriculum

Last Stone Weight II

Find the smallest possible weight of the last stone by turning the smashing game into splitting the stones into two piles as evenly as possible, solved as subset sum.

Problem Understanding

Last Stone Weight II (LeetCode 1049): each turn you pick two stones of weights x ≤ y and smash them. Equal stones both disappear; otherwise x disappears and y becomes y − x. Return the smallest possible weight of the stone left at the end, or 0 if none is left.

Examples: stones = [2,7,4,1,8,1] → 1; stones = [31,26,33,21,40] → 5.

The Intuition: Two Piles in Disguise

The order of smashes does not matter in the end, only which side each stone lands on:

  1. Every stone is added or subtracted. The last weight is (one pile) − (the other pile).
  2. Make the piles even. With total S and a pile of s ≤ S / 2, the result is S − 2s.
  3. Subset sum. Find every pile weight up to S / 2 that some subset makes, and take the largest.

Step 3 is the same table as Partition Equal Subset Sum, which asks whether s can be exactly S / 2, and Target Sum is the same plus/minus idea with a fixed target.

Interactive Walkthrough

TOTAL 23 · ONE PILE ≤ 11

BASE CASE

stones

2
7
4
1
8
1

dp

T

0

F

1

F

2

F

3

F

4

F

5

F

6

F

7

F

8

F

9

F

10

F

11

stone

:

j

:

Answer

:

 

The total is 23. One pile should be as close to 11 as possible without going over. dp[0] = true

Next

Add stone 2. Sweep j from 11 down to 2, so it is used at most once

Free preview: the first 18 of 58 steps

stones

The dp row fills with T as stones are added; at the end the scan walks down from half the total to the first T. Try Heavy stone to see the scan pass several unreachable weights.

Dry Run Table

Input: stones = [2,7,4,1,8,1], total 23, half 11.

After stone Reachable pile weights (≤ 11)
2 0, 2
7 0, 2, 7, 9
4 0, 2, 4, 6, 7, 9, 11
1 every weight from 0 to 11
8, 1 unchanged (already all reachable)

Best pile: 11, the other is 12. Answer: 12 − 11 = 1.

The Solution Template

0/1 knapsack on pile weights up to half the total, then the closest reachable weight.

Last Stone Weight II Code
1var lastStoneWeightII = function(stones) {
2 const total = stones.reduce((a, b) => a + b, 0);
3 const half = Math.floor(total / 2);
4 const dp = new Array(half + 1).fill(false);
5 dp[0] = true;
6 for (const stone of stones) {
7 for (let j = half; j >= stone; j--) {
8 dp[j] = dp[j] || dp[j - stone];
9 }
10 }
11 let best = half;
12 while (!dp[best]) best--;
13 return total - 2 * best;
14};

Edge Cases & Common Mistakes

  • One stone: nothing to smash it with; the answer is its weight (best pile 0).
  • Simulating greedily: smashing the two heaviest each time is Last Stone Weight I, and it is not optimal here.
  • Sweeping j upwards: reuses a stone, overestimating what is reachable.
  • Stone heavier than half: it never fits in the small pile; the loop simply does nothing for it.
  • Returning half − best: the answer is total − 2 · best, the gap between the two piles.

Summary

The last weight is the difference between two piles. Use a 0/1 knapsack to find the heaviest pile not above half the total, best, and return total − 2 · best. O(n · S) time, O(S) space.

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Set up

    S = sum of stones, half = S // 2, dp[0] = true.

  2. Knapsack

    For each stone, for j from half down to stone: dp[j] |= dp[j − stone].

  3. Answer

    Find the largest j ≤ half with dp[j] true and return S − 2j.

Frequently asked questions

Why is this a subset sum problem?

Each smash replaces x and y with y − x. Follow any stone through the game and it ends up added or subtracted in the final weight, so the result is (sum of one group) − (sum of the other). Every split into two groups can be produced by some order of smashes, so the question becomes: split the stones into two piles whose sums are as close as possible.


Why look for the largest pile at most half the total?

If one pile weighs s and the total is S, the other weighs S − s and the result is S − 2s. Taking s ≤ S / 2 keeps that non-negative, and it is smallest when s is as large as possible. So find the largest reachable subset sum not above S / 2.


Why does j go downwards in the inner loop?

dp[j − stone] must describe subsets that do not use the current stone yet. Sweeping j from high to low reads dp[j − stone] before this stone can have updated it, so each stone is counted at most once. An upward sweep would let one stone be reused, which is the unbounded knapsack.


How is it different from Last Stone Weight (LeetCode 1046)?

In 1046 you must always smash the two heaviest stones, so a max-heap simulates the game directly. In 1049 you choose which stones to smash, and the question is the best possible outcome, which needs the subset sum view.

New to the technique behind this problem? Watch it run step by step:

Dynamic Programming Visualization

Applying for software engineering roles? Free, no sign-up:

AI resume checker: match your resume to the job description