Skip to main content

Last Stone Weight II - Solution & Explanation

MediumArrayDynamic Programming20 min readAsked at: Amazon, Microsoft, Meta +2
Practice this problem

Problem Statement

You are given an array of integers stones where stones[i] is the weight of the ith stone.

We are playing a game with the stones. On each turn, we choose any two stones and smash them together. Suppose the stones have weights x and y with x <= y. The result of this smash is:

  • If x == y, both stones are destroyed, and
  • If x != y, the stone of weight x is destroyed, and the stone of weight y has new weight y - x.

At the end of the game, there is at most one stone left.

Return the smallest possible weight of the left stone. If there are no stones left, return 0.

 

Example 1:

Input: stones = [2,7,4,1,8,1]
Output: 1
Explanation:
We can combine 2 and 4 to get 2, so the array converts to [2,7,1,8,1] then,
we can combine 7 and 8 to get 1, so the array converts to [2,1,1,1] then,
we can combine 2 and 1 to get 1, so the array converts to [1,1,1] then,
we can combine 1 and 1 to get 0, so the array converts to [1], then that's the optimal value.

Example 2:

Input: stones = [31,26,33,21,40]
Output: 5

 

Constraints:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 100

Approach Overview

Problem Overview: You are given an array of stone weights. Each turn you smash two stones together; equal weights destroy both, otherwise the heavier one becomes the difference. The goal is to minimize the final remaining weight after all possible smashes.

The key observation: smashing stones is equivalent to partitioning the stones into two groups whose total weights are as close as possible. If one group has sum S1 and the other S2, the final weight becomes |S1 - S2|. The task reduces to finding a subset whose sum is as close as possible to half of the total weight.

Approach 1: Backtracking with Memoization (Time: O(n * sum), Space: O(n * sum))

This approach explores all ways to assign each stone to one of two groups. At index i, you decide whether to add the stone weight to the current subset or skip it. Without optimization, this forms a binary decision tree with 2^n states. Memoization stores results for states defined by (index, currentSum), avoiding repeated work.

The recursion tries to build a subset sum as close as possible to total/2. When all stones are processed, compute the difference between the two partitions. Memoization drastically reduces the search space because many recursive paths reach the same state. This approach is useful when demonstrating the transition from brute-force recursion to optimized dynamic programming and reinforces how overlapping subproblems appear in dynamic programming.

Approach 2: Dynamic Programming (Subset Sum / Knapsack) (Time: O(n * sum), Space: O(sum))

The optimal approach models the problem as a classic 0/1 knapsack variant. Compute the total weight of all stones and target half = total / 2. The goal is to find the largest achievable subset sum that does not exceed half.

Create a DP array where dp[s] indicates whether a subset sum s is achievable. Iterate through the stones and update the DP array in reverse order so each stone is used at most once. For every weight w, update dp[s] = dp[s] OR dp[s - w]. After processing all stones, scan from half downward to find the largest achievable sum.

If the best subset sum is best, the final answer becomes total - 2 * best. This works because the other partition automatically contains the remaining weight. The approach runs in linear time relative to the number of stones and the total sum, making it efficient for typical constraints. It also demonstrates a practical use of subset partitioning with arrays and state transitions commonly used in array processing problems.

Recommended for interviews: The dynamic programming subset-sum solution is the expected answer. Interviewers want to see the reduction from stone smashing to partition difference and then the application of a knapsack-style DP. Mentioning the recursive backtracking idea shows problem exploration, but implementing the optimized DP demonstrates strong mastery of dynamic programming patterns.

Approach 1: Dynamic Programming Approach

The problem can be thought of as partitioning the stones into two groups with a minimum difference in their total weights. This is similar to a variant of the knapsack problem where we try to fill a knapsack with maximum capacity, close to half of the total sum of stones.

Using dynamic programming, we define a state dp[j] which is true if a sum j can be achieved with the given stones. We traverse through the stones, updating our achievable sums. Finally, we find the largest sum that can be achieved which is less than or equal to half of the total sum of all stones. The answer will be the total sum minus twice this value.

We initialize a boolean array dp[] to track achievable sums. For each stone, we update possible sums in reverse to avoid overwriting results we still need. Finally, we check for the largest achievable sum close to half of the total sum, and the result is derived from total sum minus twice that achievable sum.

Code

C

C++

Java

Python

C#

JavaScript

Complexity

Time Complexity: O(n * sum/2), where n is the number of stones and sum is the total weight of stones.
Space Complexity: O(sum/2), where sum is the total stone weights.

Try this approach in the editor →

Approach 2: Backtracking with Memoization Approach

Another approach to solve the Last Stone Weight II problem is by using backtracking with memoization. In this approach, we try to partition the stones into two sets where the difference between the total weights of the two sets is minimized. We can recursively explore all combinations of partitioned stones while keeping track of their sums.

To optimize, we use memoization to store and reuse the results of already calculated differences for sub-problems, reducing redundant calculations and improving performance.

The solution employs a recursive helper function to explore all possible ways to partition the stones with a goal to minimize the difference between two groups. The memoization array memo[][] stores previously computed values to avoid repeated calculations, thereby optimizing the recursive calls.

Code

C

C++

Java

Python

C#

JavaScript

Complexity

Time Complexity: O(n * total_sum), where n is the length of stones and total_sum is the sum of all stone weights.
Space Complexity: O(n * total_sum), for storing the memoization table.

Try this approach in the editor →

Approach 3: Default Approach

Code

Python

Java

C++

Go

Rust

JavaScript

Try this approach in the editor →

Complexity Comparison

ApproachComplexity
Dynamic Programming Approach

Time Complexity: O(n * sum/2), where n is the number of stones and sum is the total weight of stones.
Space Complexity: O(sum/2), where sum is the total stone weights.

Backtracking with Memoization Approach

Time Complexity: O(n * total_sum), where n is the length of stones and total_sum is the sum of all stone weights.
Space Complexity: O(n * total_sum), for storing the memoization table.

Default Approach—

Detailed Complexity Analysis

ApproachTimeSpaceWhen to Use
Backtracking with MemoizationO(n * sum)O(n * sum)Useful for explaining recursion and overlapping subproblems before converting to DP
Dynamic Programming (Subset Sum / Knapsack)O(n * sum)O(sum)Best practical solution; efficient when total stone weight is reasonably bounded

Video Solution

Last Stone Weight II - Leetcode 1049 - Python • NeetCodeIO • 33,594 views views

Watch 9 more video solutions →

Frequently Asked Questions

Is Last Stone Weight II easy or hard?
Last Stone Weight II is generally classified as a medium difficulty problem. The main challenge is recognizing that the stone-smashing process reduces to a partition problem and applying a knapsack-style dynamic programming solution.
How to solve Last Stone Weight II in O(n)?
A strict O(n) solution is not possible because the problem depends on achievable subset sums up to half of the total weight. The most efficient approach is O(n * sum) using dynamic programming. It iteratively builds reachable subset sums and selects the one closest to total/2.
What is the best approach for Last Stone Weight II?
The best approach uses dynamic programming with a subset-sum (0/1 knapsack) formulation. Instead of simulating stone smashing, partition the stones into two groups with sums as close as possible. Compute the largest achievable subset sum not exceeding half of the total weight. The final result becomes total - 2 * bestSubsetSum, with time complexity O(n * sum).
What data structure is used in Last Stone Weight II?
The optimal solution primarily uses a dynamic programming array (often a boolean or integer array) to track reachable subset sums. The input itself is processed as an array of integers, and the DP structure enables efficient state transitions similar to the 0/1 knapsack problem.
What is the time complexity of Last Stone Weight II?
The optimal dynamic programming solution runs in O(n * sum) time, where n is the number of stones and sum is the total weight of all stones. Space complexity can be reduced to O(sum) using a 1D DP array. Recursive backtracking without memoization would take O(2^n) time.
Last Stone Weight II Python or Java solution approach?
Both Python and Java implementations typically use the same subset-sum dynamic programming idea. Create a DP array of size total/2 + 1, iterate through each stone, and update reachable sums in reverse order. After processing all stones, find the largest achievable sum near half of the total weight.
Is Last Stone Weight II asked at Google, Amazon, or Meta?
This problem represents a classic partition and knapsack-style dynamic programming pattern commonly asked in technical interviews. Variations of subset partition problems frequently appear at companies like Amazon, Google, and Meta because they test DP state design and optimization skills.

Ready to solve this problem?

Practice Last Stone Weight II with our built-in code editor and test cases.

Practice on FleetCode